Esiste una funzione integrata (nascosta) su MS-SQL per annullare la citazione dei nomi degli oggetti?


12

A volte memorizzo nomi di oggetti (identificatori) in alcuni dei nostri database, ad esempio in alcune tabelle dei parametri. Poiché seleziono i record da queste tabelle utilizzando gli operatori di confronto '=' o 'LIKE', devo fare attenzione a memorizzare questi nomi sempre con o senza parentesi .

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

o

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

Tuttavia MS-SQL ha alcune funzioni in cui è possibile utilizzare nomi di oggetti con o senza parentesi, ad esempio la funzione OBJECT_ID (). Ho creato un esempio minimo su 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

Ora posso usare OBJECT_ID () per verificare se la tabella TEST esiste in questo modo:

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

Non importa se passo l'identificatore TEST con o senza parentesi, il parser è abbastanza intelligente da rimuovere le parentesi.

Bene, posso simularlo aggiungendo una funzione scalare che rimuove le parentesi da una stringa:

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 poi usalo in questo modo:

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  

Ma la mia domanda è:

  • Esiste una funzione integrata nascosta che rimuove le parentesi usando T-SQL?

dbfiddle qui

Risposte:


12

Esiste una funzione integrata nascosta che rimuove le parentesi usando T-SQL?

No , non usando T-SQL.

OBJECT_IDè una funzione intrinseca . È implementato direttamente nel codice eseguibile di SQL Server, non in T-SQL; e non chiama alcun T-SQL quando viene invocato.

In fase di esecuzione, l'id oggetto viene ottenuto tramite il servizio di espressione che chiama sqlmin!I4ObjIdWstr.

L'implementazione quindi passa attraverso tutti i passaggi necessari per risolvere i parametri di stringa forniti nell'ID di un oggetto nel database di riferimento.

Uno dei primi passi include la gestione di eventuali identificatori delimitati nella stringa tramite sqlmin!CbParseQuotesW. In senso stretto, questa è la funzione di codice a cui ti riferisci, ma non è accessibile direttamente da T-SQL. Contiene il seguente codice:

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

... che sono test per gestire i personaggi:

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

Il resto del processo per risolvere i parametri in un ID prevede:

  • Avvio di una transazione di sola lettura automatica
  • Verifica dei requisiti del database contenuti
  • Scorrere le possibili corrispondenze per il parametro name (utilizzando le regole di confronto corrette)
    • Nel nome del database fornito (o nel database di contesto corrente)
    • Nel nome dello schema fornito (o sys , o nello schema predefinito dell'utente ecc.)
  • Utilizzo dei blocchi di metadati richiesti
  • Consultare la cache dei metadati per una partita
  • Recupero dei metadati nella cache, se necessario
  • Verifica delle autorizzazioni (per accedere all'ID oggetto)
  • Restituzione dell'id del primo oggetto corrispondente (se presente)

In una nota a margine, il codice nella domanda:

IF OBJECT_ID('TEST') IS NOT NULL

... non cerca solo le tabelle. Per questo sarebbe necessario utilizzare il secondo parametro della funzione. Inoltre, cerca solo qualsiasi oggetto con ambito schema denominato TEST, quindi una vista denominata BananaSchema.TEST corrisponderebbe, ad esempio. Un'espressione migliore sarebbe:

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

Domande e risposte correlate:


18

A volte conservo i nomi degli oggetti in alcuni dei nostri database

Devo aver cura di memorizzare questi nomi sempre con o senza parentesi.

Un "nome oggetto" è tecnicamente chiamato un identificatore . In alcuni contesti apparirà un identificatore codice TSQL circondato da [e] o "e". Questi personaggi non fanno parte dell'identificatore e non dovresti mai salvarli.

Conservare invece l'identificatore come nvarchar (128) (o sysname) e aggiungere i delimitatori in fase di esecuzione utilizzando la funzione QUOTENAME .

L'inverso di QUOTENAME è PARSENAME , che ha la possibilità aggiuntiva di navigare in più parti.

Si noti che QUOTENAME ha un secondo parametro facoltativo e se si specifica un singolo carattere di virgoletta per quel parametro QUOTENAME non crea un'espressione di identificatore delimitato valida. Emette un'espressione letterale varchar.


7

SQL Server ha ovviamente qualcosa di interno che viene rimosso [square brackets](o altri identificatori, come "double quotes").

Quando crei una tabella come [dbo].[foo], hai ragione, fooviene memorizzata solo in sys.tablese sys.objects, e non c'è da lamentarsi che lo schema [dbo](con le parentesi quadre) non è stato trovato.

Ma questo accade all'interno del codice per CREATE TABLE. Potrebbero usare PARSENAME(), come ha sottolineato David. Collegare il debugger potrebbe indicare con certezza, ma importa?

Puoi guardare altrove per vedere cosa stanno facendo, e sys.sp_renamein effetti rende ciò che PARSENAME()viene utilizzato:

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

Ma ancora una volta, non sono sicuro di capire perché vuoi rimuovere solo a volte le parentesi quadre.

Per me, personalmente, una percentuale abbastanza grande del mio codice è scritta per un pubblico più ampio, che utilizzerà il codice in ambienti in cui non ho alcuna conoscenza o controllo sul fatto che stiano usando identificativi non sicuri. Quindi ho sempre e scriverò sempre (e preferirò) il codice che usa QUOTENAME()per generare script che includano qualsiasi tipo di identificatore.

Preferirei di gran lunga avere le parentesi quadre per tutto il tempo, piuttosto che toglierle e mordermi una volta che erano necessarie.


2
@McNets - Per il numero minuscolo di casi in cui questo potrebbe essere utile, preferirei che si concentrassero su altre cose. Puoi usare abbastanza facilmente le funzioni di stringa esistenti per eliminare le tracce iniziali [e finali ]e sostituirle ]]con]
Martin Smith

-6

Quando sono necessarie parentesi quadre - è perché il tuo identificatore è già una parola chiave riservata. Usarli arbitrariamente non solo non è necessario, ma crea molta confusione, come puoi vedere.

A mio avviso, il modo migliore è quello di evitare di utilizzare identificatori riservati in primo luogo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.