Existe alguma função interna (oculta) no MS-SQL para citar nomes de objetos?

12

Às vezes, guardo nomes de objetos (identificadores) em alguns de nossos bancos de dados, por exemplo, em algumas tabelas de parâmetros. Como seleciono registros dessas tabelas usando os operadores de comparação '=' ou 'LIKE', devo ter o cuidado de armazenar esses nomes sempre com ou sem colchetes .

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

ou

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

No entanto, o MS-SQL possui algumas funções nas quais você pode usar nomes de objetos com ou sem colchetes, por exemplo, a função OBJECT_ID (). Eu configurei um exemplo mínimo no 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

Agora eu posso usar OBJECT_ID () para verificar se a tabela TEST existe desta maneira:

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. |

Não importa se eu passar o identificador TEST com ou sem colchetes, o analisador é inteligente o suficiente para removê-los.

Bem, eu posso simular isso adicionando uma função escalar que remove colchetes de uma string:

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

E então use-o desta maneira:

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  

Mas minha pergunta é:

  • Existe alguma função interna oculta que remove os colchetes usando o T-SQL?

dbfiddle aqui

McNets
fonte

Respostas:

12

Existe alguma função interna oculta que remove os colchetes usando o T-SQL?

Não , não usando T-SQL.

OBJECT_IDé uma função intrínseca . É implementado diretamente no código executável do SQL Server, não no T-SQL; e não chama nenhum T-SQL quando invocado.

No tempo de execução, o ID do objeto é obtido por meio da chamada do serviço de expressão sqlmin!I4ObjIdWstr.

A implementação passa por todas as etapas necessárias para resolver o (s) parâmetro (s) de sequência fornecido (s) no ID de um objeto no banco de dados referenciado.

Uma das primeiras etapas inclui lidar com quaisquer identificadores delimitados na string via sqlmin!CbParseQuotesW. Em um sentido restrito, essa é a função de código a que você está se referindo, mas não é acessível diretamente no T-SQL. Ele contém o seguinte código:

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

... que são testes para manipular os caracteres:

  • hex 22 = dec 34 = "
  • hex 2E = dec 46 = .
  • hex 5B = dec 91 = [
  • hex 5D = dec 93 = ]

O restante do processo para resolver os parâmetros para um ID envolve:

  • Iniciando uma transação somente leitura automática
  • Verificando requisitos de banco de dados contidos
  • Iterando através de possíveis correspondências para o parâmetro name (usando o agrupamento correto)
    • No nome do banco de dados fornecido (ou banco de dados de contexto atual)
    • No nome do esquema fornecido (ou sys , ou esquema padrão do usuário etc.)
  • Tomando os bloqueios de metadados necessários
  • Consultando o cache de metadados para uma correspondência
  • Buscando metadados no cache, se necessário
  • Verificando permissões (para acessar o ID do objeto)
  • Retornando o ID do primeiro objeto correspondido (se houver)

Em uma nota lateral, o código na pergunta:

IF OBJECT_ID('TEST') IS NOT NULL

... não procura apenas tabelas. Para isso seria necessário o uso do segundo parâmetro de função. Além disso, ele só olha para qualquer objeto de teste chamado de escopo do esquema - para uma vista nomeada BananaSchema.TEST iria corresponder, por exemplo. Uma expressão melhor seria:

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

Perguntas e Respostas relacionadas:

Paul White 9
fonte
18

Às vezes, guardo nomes de objetos em alguns de nossos bancos de dados

Devo ter o cuidado de armazenar esses nomes sempre com ou sem colchetes.

Um "nome do objeto" é tecnicamente chamado de identificador . Em alguns contextos, um identificador aparecerá com o código TSQL cercado por [e] ou "e". Esses caracteres não fazem parte do identificador e você nunca deve armazená-los.

Em vez disso, armazene o identificador como um nvarchar (128) (ou sysname) e adicione os delimitadores em tempo de execução usando a função QUOTENAME .

O inverso de QUOTENAME é PARSENAME , que possui a capacidade adicional de navegar por nomes de várias partes.

Observe que QUOTENAME possui um segundo parâmetro opcional e, se você especificar um caractere de aspas simples para esse parâmetro, QUOTENAME não criará uma expressão identificadora delimitada válida. Emite uma expressão literal varchar.

David Browne - Microsoft
fonte
7

O SQL Server obviamente possui algo interno que retira [square brackets](ou outros identificadores, como "double quotes").

Quando você cria uma tabela como [dbo].[foo], você está certo, apenas fooé armazenado sys.tablese sys.objects, e não há como reclamar que o esquema [dbo](com colchetes) não foi encontrado.

Mas isso acontece dentro do código para CREATE TABLE. Eles podem estar usando PARSENAME(), como David apontou. Ligar o depurador pode indicar com certeza, mas isso importa?

Você pode procurar em outro lugar para ver o que eles estão fazendo e sys.sp_rename, de fato, produz o que PARSENAME()é usado:

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

Mas, novamente, não sei se entendi por que você deseja remover apenas os colchetes às vezes .

Para mim, pessoalmente, uma porcentagem grande o suficiente do meu código é escrita para um público mais amplo, que usará o código em ambientes onde eu não tenho conhecimento ou controle sobre se eles estão usando identificadores inseguros. Então, eu sempre escrevo (e prefiro) código que usa QUOTENAME()para gerar scripts que incluem qualquer tipo de identificador.

Eu preferiria ter os colchetes lá o tempo todo, do que levá-los embora e ser mordidos na hora em que eram necessários.

Aaron Bertrand
fonte
2
@ McNets - Para o número minúsculo de casos em que isso poderia ser útil, prefiro que eles se concentrem em outras coisas. Você pode facilmente usar funções de cadeia existente para retirar esquerda e à direita [e ]e substituir qualquer ]]com]
Martin Smith
-6

Quando colchetes são necessários , é porque seu identificador já é uma palavra-chave reservada. Usá-los arbitrariamente não só não é necessário, mas também gera muita confusão, como você pode ver.

Na minha opinião, a melhor maneira é evitar o uso de identificadores reservados em primeiro lugar.

jinzai
fonte