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

McN*_*ets 12 sql-server t-sql

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

IF EXISTS (SELECT 1 FROM MYTABLE WHERE OBJ_NAME = '[TABLE_NAME]';
Run Code Online (Sandbox Code Playgroud)

或者

IF EXISTS (SELECT 1 FROM MYTABLE WHERE OBJ_NAME = 'TABLE_NAME';
Run Code Online (Sandbox Code Playgroud)

但是,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
Run Code Online (Sandbox Code Playgroud)

现在我可以使用 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. |
Run Code Online (Sandbox Code Playgroud)

我是否通过带或不带括号的标识符 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
Run Code Online (Sandbox Code Playgroud)

然后以这种方式使用它:

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  
Run Code Online (Sandbox Code Playgroud)

但我的问题是:

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

dbfiddle在这里

Dav*_*oft 18

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

我必须小心地存储这些名称,无论有没有括号。

“对象名称”在技术上称为标识符。在某些情况下,标识符会出现在 TSQL 代码中,被 [ 和 ] 或“和”包围。这些字符不是标识符的一部分,您不应该存储它们。

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

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

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


Pau*_*ite 12

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

,不使用 T-SQL。

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

在运行时,对象 id 是通过表达式服务调用获得的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
Run Code Online (Sandbox Code Playgroud)

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

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

将参数解析为 id 的其余过程包括:

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

附带说明一下,问题中的代码:

IF OBJECT_ID('TEST') IS NOT NULL
Run Code Online (Sandbox Code Playgroud)

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

IF OBJECT_ID(N'dbo.TEST', N'U') IS NOT NULL
Run Code Online (Sandbox Code Playgroud)

相关问答:


Aar*_*and 7

SQL Server 显然有一些内部的东西可以去掉[square brackets](或其他标识符,比如"double quotes")。

当您创建一个类似于 的表时[dbo].[foo],您是对的,只会foo存储在sys.tablesand 中sys.objects,并且没有人抱怨找不到架构[dbo](带方括号)。

但这发生在CREATE TABLE. PARSENAME()正如大卫指出的那样,他们可能正在使用。连接调试器可以肯定地表明,但这有关系吗?

你可以在别处看看他们在做什么,sys.sp_rename实际上确实产生了PARSENAME()使用:

select @UnqualOldName = parsename(@objname, 1),
        @QualName1 = parsename(@objname, 2),
        @QualName2 = parsename(@objname, 3),
        @QualName3 = parsename(@objname, 4)
Run Code Online (Sandbox Code Playgroud)

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

就我个人而言,我的代码中有足够大的百分比是为更广泛的受众编写的,他们将在我不知道或无法控制他们是否使用不安全标识符的环境中使用代码。所以我总是并且总是会编写(并且更喜欢)QUOTENAME()用于生成包含任何类型标识符的脚本的代码。

我宁愿一直把方括号放在那里,也不愿把它们拿走,并在需要它们的时候被咬伤。

  • @McNets - 对于这可能有用的极少数情况,我宁愿他们专注于其他事情。你可以很容易地使用现有的字符串函数来去除前导和尾随的 `[` 和 `]`,并用 `]` 替换任何 `]]` (2认同)