如何检查建议的 T-SQL 对象名称是否有效或是否需要用方括号括起来
How to check if a proposed T-SQL object name is valid or needs to be enclosed in square brackets
可以向我的存储过程传递任意字符串 @String
作为 varchar
或 nvarchar
,长度不超过 128 个字符。
存储过程需要使用该 @String
值作为对象名称,即 table、字段、视图或类似名称。
我如何确定所提供的 @String
的值本身是否是一个有效的对象名称 - 即不是保留字并且不包含无效字符 - 或者它是否需要包含在(和转义任何包含的)方括号,以便使成为有效的对象名称。
例如,"X"、"Foo" 和 "C:\Temp\X.csv" 是有效的对象名称(是的,我已经成功地按原样使用了 "C:\Temp\X.csv")并且可以使用未修改,而 "Select"、"AB]C" 和“1_ABC”无效,需要更改为“[Select]”、“[AB]]C] ”和“[1_ABC]”
我更喜欢不会的解决方案只是尝试创建一个具有该名称的对象,然后检查它是否有效。
将优先考虑适用于更广泛 SQL 服务器版本的答案,而不仅仅是最新版本,较短、不太复杂的代码将优先于更长、更复杂的代码。
作为导致我问这个问题的问题的一个例子,我的 SP 可以传递一个 @Name
,它可能是 "C:\Temp\X.csv" 或 "ABC" 或 "SELECT" 管他呢。我的 SP 然后创建一个 table,填充它并 然后 (所以我不必使用动态 SQL)使用 [= 将它的名称更改为 @Name 17=]。我需要 QUOTENAME(@Name)
where @Name = "SELECT",但在其他两种情况下不需要,产生(在 SSMS 对象资源管理器中)tables "dbo.C:\Temp\X.csv"、"dbo.ABC" 和 "dbo.SELECT"。但是,在 运行 SP 的其余部分之前,我还需要检查这些对象是否存在,如果我 QUOTENAME
"C:\Temp\X.csv",则在将其作为新名称传递给 [=17] 之前=],它在SSMS对象资源管理器中显示为"dbo.[C:\Temp\X.csv]",我必须使用IF OBJECT_ID('[[C:\Temp\X.csv]]]') IS NOT NULL
才能成功确定它存在,不是IF OBJECT_ID('[C:\Temp\X.csv]') IS NOT NULL
。
使用 sp_rename
时,对象的新名称永远不需要转义,因此只需使用传入的 @Name
值即可。
确实 OBJECT_ID
有时可能需要转义名称,但由于有更直接的测试可用,同样不需要转义,我建议您改用它作为测试:
IF EXISTS(SELECT * from sys.tables where name = @Name)
...
(或者您可以查询 sys.objects
,由您决定)
我最初设想您使用的是动态 SQL 中的名称。在动态 SQL 上下文中,我建议总是使用 QUOTENAME
转义名称,而不是尝试确定它是否需要转义。
此代码可能有助于了解何时使用 QUOTENAME。它未在 sp_rename.
中使用
CREATE PROCEDURE TestObjectNames
@Name SYSNAME
AS
DECLARE @SQL NVARCHAR(MAX);
-- Create temporary table
CREATE TABLE Temp (ID INT, Name SYSNAME);
-- Load temporary table
INSERT INTO Temp SELECT database_id, name FROM sys.databases;
-- Check existance of table
IF NOT EXISTS(SELECT * FROM sys.tables WHERE name = @Name)
-- Rename table (Don't use QUOTENAME)
EXEC sp_rename 'Temp', @Name;;
-- Fetch object id of new table (Use QUOTENAME)
SELECT OBJECT_ID(QUOTENAME(@Name)) AS NewTableID;
-- Select from table dynamically (Use QUOTENAME)
SET @SQL = 'SELECT COUNT(*) AS NewTableCount FROM ' + QUOTENAME(@Name);
EXEC sp_executesql @SQL;
-- Drop new table dynamically (Use QUOTENAME)
SET @SQL = 'DROP TABLE ' + QUOTENAME(@Name);
EXEC sp_executesql @SQL;
GO
EXEC TestObjectNames 'SELECT';
EXEC TestObjectNames 'C:\Temp\X.csv';
EXEC TestObjectNames 'AB]C';
EXEC TestObjectNames '1_ABC';
EXEC TestObjectNames 'ABC';
可以向我的存储过程传递任意字符串 @String
作为 varchar
或 nvarchar
,长度不超过 128 个字符。
存储过程需要使用该 @String
值作为对象名称,即 table、字段、视图或类似名称。
我如何确定所提供的 @String
的值本身是否是一个有效的对象名称 - 即不是保留字并且不包含无效字符 - 或者它是否需要包含在(和转义任何包含的)方括号,以便使成为有效的对象名称。
例如,"X"、"Foo" 和 "C:\Temp\X.csv" 是有效的对象名称(是的,我已经成功地按原样使用了 "C:\Temp\X.csv")并且可以使用未修改,而 "Select"、"AB]C" 和“1_ABC”无效,需要更改为“[Select]”、“[AB]]C] ”和“[1_ABC]”
我更喜欢不会的解决方案只是尝试创建一个具有该名称的对象,然后检查它是否有效。
将优先考虑适用于更广泛 SQL 服务器版本的答案,而不仅仅是最新版本,较短、不太复杂的代码将优先于更长、更复杂的代码。
作为导致我问这个问题的问题的一个例子,我的 SP 可以传递一个 @Name
,它可能是 "C:\Temp\X.csv" 或 "ABC" 或 "SELECT" 管他呢。我的 SP 然后创建一个 table,填充它并 然后 (所以我不必使用动态 SQL)使用 [= 将它的名称更改为 @Name 17=]。我需要 QUOTENAME(@Name)
where @Name = "SELECT",但在其他两种情况下不需要,产生(在 SSMS 对象资源管理器中)tables "dbo.C:\Temp\X.csv"、"dbo.ABC" 和 "dbo.SELECT"。但是,在 运行 SP 的其余部分之前,我还需要检查这些对象是否存在,如果我 QUOTENAME
"C:\Temp\X.csv",则在将其作为新名称传递给 [=17] 之前=],它在SSMS对象资源管理器中显示为"dbo.[C:\Temp\X.csv]",我必须使用IF OBJECT_ID('[[C:\Temp\X.csv]]]') IS NOT NULL
才能成功确定它存在,不是IF OBJECT_ID('[C:\Temp\X.csv]') IS NOT NULL
。
使用 sp_rename
时,对象的新名称永远不需要转义,因此只需使用传入的 @Name
值即可。
确实 OBJECT_ID
有时可能需要转义名称,但由于有更直接的测试可用,同样不需要转义,我建议您改用它作为测试:
IF EXISTS(SELECT * from sys.tables where name = @Name)
...
(或者您可以查询 sys.objects
,由您决定)
我最初设想您使用的是动态 SQL 中的名称。在动态 SQL 上下文中,我建议总是使用 QUOTENAME
转义名称,而不是尝试确定它是否需要转义。
此代码可能有助于了解何时使用 QUOTENAME。它未在 sp_rename.
中使用CREATE PROCEDURE TestObjectNames
@Name SYSNAME
AS
DECLARE @SQL NVARCHAR(MAX);
-- Create temporary table
CREATE TABLE Temp (ID INT, Name SYSNAME);
-- Load temporary table
INSERT INTO Temp SELECT database_id, name FROM sys.databases;
-- Check existance of table
IF NOT EXISTS(SELECT * FROM sys.tables WHERE name = @Name)
-- Rename table (Don't use QUOTENAME)
EXEC sp_rename 'Temp', @Name;;
-- Fetch object id of new table (Use QUOTENAME)
SELECT OBJECT_ID(QUOTENAME(@Name)) AS NewTableID;
-- Select from table dynamically (Use QUOTENAME)
SET @SQL = 'SELECT COUNT(*) AS NewTableCount FROM ' + QUOTENAME(@Name);
EXEC sp_executesql @SQL;
-- Drop new table dynamically (Use QUOTENAME)
SET @SQL = 'DROP TABLE ' + QUOTENAME(@Name);
EXEC sp_executesql @SQL;
GO
EXEC TestObjectNames 'SELECT';
EXEC TestObjectNames 'C:\Temp\X.csv';
EXEC TestObjectNames 'AB]C';
EXEC TestObjectNames '1_ABC';
EXEC TestObjectNames 'ABC';