Czy w MS-SQL jest jakaś (ukryta) funkcja wbudowana do cofania cudzysłowu nazw obiektów?

12

Czasami przechowuję nazwy obiektów (identyfikatory) w niektórych naszych bazach danych, na przykład w niektórych tabelach parametrów. Ponieważ wybieram rekordy z tych tabel za pomocą operatorów porównania „=” lub „LIKE”, muszę zadbać o to, aby te nazwy były zawsze przechowywane w nawiasach kwadratowych lub bez nich .

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

lub

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

Jednak MS-SQL ma pewne funkcje, w których można używać nazw obiektów ze nawiasami lub bez, na przykład funkcję OBJECT_ID (). Mam ustawiony minimalny przykład na 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

Teraz mogę użyć OBJECT_ID (), aby sprawdzić, czy tabela TEST istnieje w ten sposób:

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

Nie ma znaczenia, czy przejdę identyfikator TEST z nawiasami, czy bez, parser jest wystarczająco inteligentny, aby usunąć nawiasy.

Cóż, mogę to zasymulować, dodając funkcję skalarną, która usuwa nawiasy z jednego ciągu:

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

A następnie użyj go w ten sposób:

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  

Ale moje pytanie brzmi:

  • Czy jest jakaś ukryta funkcja wbudowana, która usuwa nawiasy za pomocą T-SQL?

dbfiddle tutaj

McNets
źródło

Odpowiedzi:

12

Czy jest jakaś ukryta funkcja wbudowana, która usuwa nawiasy za pomocą T-SQL?

Nie , nie używając T-SQL.

OBJECT_IDjest funkcją wewnętrzną . Jest zaimplementowany bezpośrednio w kodzie wykonywalnym SQL Server, a nie w T-SQL; i po wywołaniu nie wywołuje żadnego języka T-SQL.

W czasie wykonywania identyfikator obiektu jest uzyskiwany przez wywołanie usługi wyrażania sqlmin!I4ObjIdWstr.

Następnie implementacja przechodzi wszystkie niezbędne kroki w celu przetłumaczenia podanych parametrów ciągu na identyfikator obiektu w przywoływanej bazie danych.

Jednym z pierwszych kroków jest postępowanie z dowolnymi ogranicznikami w ciągu poprzez sqlmin!CbParseQuotesW. W wąskim znaczeniu jest to funkcja kodu, do której się odwołujesz, ale nie jest ona dostępna bezpośrednio z T-SQL. Zawiera następujący kod:

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

... które są testami do obsługi znaków:

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

Reszta procesu przetwarzania parametrów na identyfikator obejmuje:

  • Rozpoczęcie automatycznej transakcji tylko do odczytu
  • Sprawdzanie zawartych wymagań dotyczących bazy danych
  • Iteracja przez możliwe dopasowania parametru name (przy użyciu prawidłowego sortowania)
    • W podanej nazwie bazy danych (lub bieżącej bazie danych kontekstu)
    • W dostarczonej nazwie schematu (lub sys lub domyślnym schemacie użytkownika itp.)
  • Biorąc wymagane blokady metadanych
  • Sprawdzanie pamięci podręcznej metadanych w celu dopasowania
  • Pobieranie metadanych do pamięci podręcznej, jeśli to konieczne
  • Sprawdzanie uprawnień (aby uzyskać dostęp do identyfikatora obiektu)
  • Zwracanie identyfikatora pierwszego dopasowanego obiektu (jeśli istnieje)

Na marginesie kod w pytaniu:

IF OBJECT_ID('TEST') IS NOT NULL

... nie szuka tylko tabel. Do tego wymagałoby użycia drugiego parametru funkcji. Ponadto wyszukuje tylko każdy obiekt o zasięgu schematu o nazwie TEST - więc widok o nazwie BananaSchema.TEST pasowałby na przykład. Lepszym wyrażeniem byłoby:

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

Powiązane pytania i odpowiedzi:

Paul White 9
źródło
18

Czasami przechowuję nazwy obiektów w niektórych naszych bazach danych

Muszę zadbać o to, aby te nazwy były zawsze przechowywane w nawiasach lub bez.

„Nazwa obiektu” jest technicznie nazywana identyfikatorem . W niektórych kontekstach pojawi się identyfikator TSQL otoczony tagami [i] lub „i”. Te znaki nie są częścią identyfikatora i nigdy nie należy ich przechowywać.

Zamiast tego przechowuj identyfikator jako nvarchar (128) (lub sysname) i dodaj ograniczniki w czasie wykonywania, używając funkcji QUOTENAME .

Odwrotnością QUOTENAME jest PARSENAME , która ma dodatkową możliwość nawigacji po nazwach wieloczęściowych.

Zauważ, że QUOTENAME ma opcjonalny drugi parametr, a jeśli podasz pojedynczy znak cudzysłowu dla tego parametru, QUOTENAME nie utworzy prawidłowego wyrażenia z ogranicznikiem. Emituje dosłowne wyrażenie varchar.

David Browne - Microsoft
źródło
7

SQL Server ma oczywiście coś wewnętrznego, co usuwa [square brackets](lub inne identyfikatory, jak "double quotes").

Kiedy tworzysz tabelę [dbo].[foo], masz rację, tylko foojest przechowywany w sys.tablesi sys.objects, i nie ma narzekań, że schemat [dbo](z nawiasami kwadratowymi) nie został znaleziony.

Ale dzieje się tak w kodzie CREATE TABLE. Mogą używać PARSENAME(), jak zauważył David. Podłączenie debuggera może na pewno oznaczać, ale czy to ma znaczenie?

Możesz poszukać gdzie indziej, aby zobaczyć, co robią, a sys.sp_renamefaktycznie uzyskuje PARSENAME()się używane:

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

Ale znowu, nie jestem pewien, czy rozumiem, dlaczego chcesz czasami usuwać nawiasy kwadratowe.

Dla mnie osobiście wystarczająca część mojego kodu jest napisana dla szerszego grona odbiorców, którzy będą go używać w środowiskach, w których nie mam wiedzy ani kontroli nad tym, czy używają niebezpiecznych identyfikatorów. Więc zawsze mam i zawsze napiszę (i preferuję) kod, który używa QUOTENAME()do generowania skryptów zawierających dowolny rodzaj identyfikatora.

Wolałbym zawsze mieć nawiasy kwadratowe tam przez cały czas, niż zabierać je i gryźć za jednym razem, gdy były potrzebne.

Aaron Bertrand
źródło
2
@McNets - W przypadku niewielkiej liczby przypadków, w których może to być przydatne, wolę skupić się na innych rzeczach. Dość łatwo można wykorzystać istniejące funkcje ciągów zdejmować początkowe i końcowe [oraz ]i zastąpić dowolny ]]z]
Martin Smith
-6

Kiedy potrzebne są nawiasy kwadratowe - dzieje się tak, ponieważ twój identyfikator jest już zarezerwowanym słowem kluczowym. Używanie ich arbitralnie nie jest nie tylko konieczne, ale prowadzi do wielu nieporozumień.

Moim zdaniem najlepszym sposobem jest uniknięcie używania zastrzeżonych identyfikatorów.

Jinzai
źródło