MS-SQL上是否有任何(隐藏的)内置函数来取消引用对象名称?


12

有时,我在某些数据库中存储对象名称(标识符),例如在某些参数表中。因为我使用'='或'LIKE'比较运算符从这些表中选择记录,所以我必须注意始终使用括号这些名称存储起来。

IF EXISTS (SELECT 1 FROM MYTABLE WHERE OBJ_NAME = '[TABLE_NAME]';

要么

IF EXISTS (SELECT 1 FROM MYTABLE WHERE OBJ_NAME = 'TABLE_NAME';

但是,MS-SQL具有一些函数,您可以在其中使用带括号或不带括号的对象名称,例如OBJECT_ID()函数。我在dbfiddle.uk上建立了一个最小的示例。

CREATE TABLE TEST
(
    ID     INT IDENTITY(1,1) PRIMARY KEY,
    OBJECT sysname NOT NULL
);
GO

INSERT INTO TEST VALUES ('[obj1]'),('obj2'),('obj3'),('[obj4]');
GO

现在,我可以使用OBJECT_ID()来检查表TEST是否以这种方式存在:

IF OBJECT_ID('TEST') IS NOT NULL
BEGIN
    SELECT 'TEST EXISTS.' OBJECT_ID;
END
GO

| OBJECT_ID    |
| :----------- |
| TEST EXISTS. |

IF OBJECT_ID('[TEST]') IS NOT NULL
BEGIN
    SELECT '[TEST] EXISTS.' OBJECT_ID;
END
GO

| OBJECT_ID      |
| :------------- |
| [TEST] EXISTS. |

无论是否通过标识符TEST(带括号或不带括号),解析器都足够聪明,可以删除括号。

好吧,我可以通过添加一个从一个字符串中删除括号的标量函数来模拟这一点:

CREATE FUNCTION UNQUOTENAME(@TXT NVARCHAR(MAX)) 
RETURNS NVARCHAR(MAX)
AS
    BEGIN
        RETURN IIF(LEFT(@TXT, 1) = N'[' AND RIGHT(@TXT, 1) = N']', 
                   SUBSTRING(@TXT, 2, LEN(@TXT) -  2), 
                   @TXT);
    END;
GO

然后以这种方式使用它:

SELECT dbo.UNQUOTENAME (N'[FIELD]') NAME1, N'FIELD' NAME2;
GO

NAME1 | NAME2
:---- | :----
FIELD | FIELD

SELECT ID, OBJECT 
FROM   TEST 
WHERE OBJECT LIKE 'obj%';
GO

ID | OBJECT
-: | :-----
 2 | obj2  
 3 | obj3  

SELECT ID, dbo.UNQUOTENAME(OBJECT) 
FROM   TEST 
WHERE  dbo.UNQUOTENAME(OBJECT) LIKE 'obj%';
GO

ID | (No column name)
-: | :---------------
 1 | obj1
 2 | obj2
 3 | obj3
 4 | obj4  

但是我的问题是:

  • 是否有任何隐藏的内置函数可以使用T-SQL删除括号?

dbfiddle 在这里

Answers:


12

是否有任何隐藏的内置函数可以使用T-SQL删除括号?

,不使用T-SQL。

OBJECT_ID是一个内在函数。它是直接在SQL Server可执行代码中实现的,而不是在T-SQL中实现的;并且在调用时不会调用任何T-SQL。

在运行时,对象ID是通过表达式服务call获得的sqlmin!I4ObjIdWstr

然后,实现将执行所有必要步骤,以将提供的字符串参数解析为引用数据库中对象的ID。

第一步之一是通过来处理字符串中任何定界的标识符sqlmin!CbParseQuotesW。从狭义上讲,这就是您所指的代码功能,但不能直接从T-SQL访问。它包含以下代码:

cmp     r9d,22h
je      sqlmin!CbParseQuotesW+0x185
cmp     r9d,2Eh
je      sqlmin!CbParseQuotesW+0x139
cmp     r9d,5Bh
je      sqlmin!CbParseQuotesW+0xfe
cmp     r9d,5Dh
je      sqlmin!CbParseQuotesW+0xda

...是处理字符的测试:

  • 十六进制22 =十二月34 = "
  • 十六进制2E =十月46 = .
  • 十六进制5B =十月91 = [
  • 十六进制5D = dec 93 = ]

将参数解析为id的其余过程涉及:

  • 开始自动只读事务
  • 检查包含的数据库要求
  • 遍历name参数的可能匹配项(使用正确的排序规则)
    • 在提供的数据库名称(或当前上下文数据库)中
    • 在提供的架构名称(或sys,或用户的默认架构等)中
  • 获取所需的元数据锁
  • 咨询元数据缓存以进行匹配
  • 如有必要,将元数据提取到缓存中
  • 检查权限(访问对象ID)
  • 返回第一个匹配对象的ID(如果有)

另外,问题中的代码:

IF OBJECT_ID('TEST') IS NOT NULL

...不仅只寻找表格。为此,需要使用第二个功能参数。此外,它仅查找任何名为TEST的架构范围的对象-例如,名为BananaSchema.TEST的视图将匹配。更好的表达是:

IF OBJECT_ID(N'dbo.TEST', N'U') IS NOT NULL

相关问答:


18

有时我在一些数据库中存储对象名称

我必须注意始终使用方括号或不使用方括号来存储此名称。

“对象名称”在技术上称为标识符。在某些情况下,标识符将显示为TSQL代码,并用[和]或“和”包围。这些字符不是标识符的一部分,并且永远不要存储它们。

而是将标识符存储为nvarchar(128)(或sysname),并在运行时使用QUOTENAME函数添加定界符。

QUOTENAME的倒数是PARSENAME,它具有导航多部分名称的附加功能。

请注意,QUOTENAME具有可选的第二个参数,并且如果您为该参数指定单引号字符,则QUOTENAME不会创建有效的带分隔符的标识符表达式。它发出一个varchar文字表达式。


7

很明显,SQL Server 内部有一些东西可以剥离[square brackets](或其他标识符,例如"double quotes")。

当您创建类似的表时[dbo].[foo],您是对的,仅foo将其存储在sys.tables和中sys.objects,并且不会抱怨找不到架构[dbo](带有方括号)。

但这发生在的代码内部CREATE TABLEPARSENAME()正如David指出的那样,他们可能正在使用。连接调试器可以肯定地指示,但这有关系吗?

您可以在其他地方查看他们在做什么,并且sys.sp_rename实际上会产生PARSENAME()所使用的收益:

select @UnqualOldName = parsename(@objname, 1),
        @QualName1 = parsename(@objname, 2),
        @QualName2 = parsename(@objname, 3),
        @QualName3 = parsename(@objname, 4)

但同样,我不确定我是否理解为什么有时只删除方括号。

对我个人而言,我的代码中有很大一部分是为更广泛的读者编写的,他们将在我不了解或无法控制他们是否使用不安全标识符的环境中使用该代码。因此,我将始终(并且倾向于)编写(并喜欢)QUOTENAME()用于生成包含任何种类的标识符的脚本的代码。

我宁愿始终将方括号放在那儿,而不是将它们拿走并在需要的时候被咬住。


2
@McNets-对于可能有用的极少数情况,我宁愿他们专注于其他事情。您可以很容易地使用现有的字符串函数来去除前导和尾随[]]]]
马丁

-6

需要方括号时-这是因为您的标识符已经是保留关键字。如您所见,随意使用它们不仅是不必要的,而且还会引起很多混乱。

我认为,最好的方法是首先避免使用保留的标识符。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.