Existe-t-il une fonction intégrée (masquée) sur MS-SQL pour supprimer les noms d'objets?

12

Parfois, je stocke des noms d'objets (identificateurs) dans certaines de nos bases de données, par exemple dans certaines tables de paramètres. Étant donné que je sélectionne les enregistrements de ces tables à l'aide des opérateurs de comparaison «=» ou «LIKE», je dois prendre soin de stocker ces noms toujours avec ou sans crochets .

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

ou

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

Cependant, MS-SQL a certaines fonctions où vous pouvez utiliser des noms d'objet avec ou sans crochets, par exemple la fonction OBJECT_ID (). J'ai mis en place un exemple minimal sur 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

Maintenant, je peux utiliser OBJECT_ID () pour vérifier si la table TEST existe de cette façon:

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

Peu importe que je passe l'identifiant TEST avec ou sans crochets, l'analyseur est assez intelligent pour supprimer les crochets.

Eh bien, je peux simuler cela en ajoutant une fonction scalaire qui supprime les crochets d'une chaîne:

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

Et puis utilisez-le de cette manière:

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  

Mais ma question est:

  • Existe-t-il une fonction intégrée cachée qui supprime les crochets à l'aide de T-SQL?

dbfiddle ici

McNets
la source

Réponses:

12

Existe-t-il une fonction intégrée cachée qui supprime les crochets à l'aide de T-SQL?

Non , ne pas utiliser T-SQL.

OBJECT_IDest une fonction intrinsèque . Il est implémenté directement dans le code exécutable SQL Server, pas dans T-SQL; et il n'appelle aucun T-SQL lorsqu'il est appelé.

Au moment de l'exécution, l'ID d'objet est obtenu via l'expression de service appelant sqlmin!I4ObjIdWstr.

L'implémentation passe ensuite par toutes les étapes nécessaires pour résoudre le ou les paramètres de chaîne fournis dans l'ID d'un objet dans la base de données référencée.

L'une des premières étapes consiste à traiter tous les identificateurs délimités dans la chaîne via sqlmin!CbParseQuotesW. Dans un sens étroit, c'est la fonction de code à laquelle vous faites référence, mais elle n'est pas accessible directement à partir de T-SQL. Il contient le code suivant:

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

... qui sont des tests pour gérer les personnages:

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

Le reste du processus de résolution des paramètres en identifiant implique:

  • Démarrage d'une transaction en lecture seule automatique
  • Vérification des exigences de la base de données contenue
  • Itération à travers les correspondances possibles pour le paramètre de nom (en utilisant le classement correct)
    • Dans le nom de base de données fourni (ou la base de données de contexte actuelle)
    • Dans le nom de schéma fourni (ou sys , ou le schéma par défaut de l'utilisateur, etc.)
  • Prendre les verrous de métadonnées requis
  • Consultation du cache de métadonnées pour une correspondance
  • Récupération des métadonnées dans le cache si nécessaire
  • Vérification des autorisations (pour accéder à l'ID d'objet)
  • Renvoyer l'ID du premier objet correspondant (le cas échéant)

En passant, le code de la question:

IF OBJECT_ID('TEST') IS NOT NULL

... ne cherche pas seulement des tableaux. Pour cela, il faudrait utiliser le deuxième paramètre de fonction. En outre, il ne recherche que tout objet à portée de schéma nommé TEST - une vue nommée BananaSchema.TEST correspondrait, par exemple. Une meilleure expression serait:

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

Questions et réponses connexes:

Paul White 9
la source
18

Parfois, je stocke des noms d'objets dans certaines de nos bases de données

Je dois prendre soin de stocker ces noms toujours avec ou sans crochets.

Un "nom d'objet" est techniquement appelé un identifiant . Dans certains contextes, un identifiant apparaîtra code TSQL entouré de [et] ou "et". Ces caractères ne font pas partie de l'identifiant et vous ne devez jamais les stocker.

Au lieu de cela, stockez l'identifiant en tant que nvarchar (128) (ou sysname) et ajoutez les délimiteurs au moment de l'exécution à l'aide de la fonction QUOTENAME .

L'inverse de QUOTENAME est PARSENAME , qui a la possibilité supplémentaire de parcourir les noms en plusieurs parties.

Notez que QUOTENAME a un deuxième paramètre facultatif et si vous spécifiez un seul caractère de guillemet pour ce paramètre, QUOTENAME ne crée pas d'expression d'expression délimitée valide. Il émet une expression littérale varchar.

David Browne - Microsoft
la source
7

SQL Server a évidemment quelque chose interne qui se retire [square brackets](ou d'autres identifiants, comme "double quotes").

Lorsque vous créez une table comme [dbo].[foo], vous avez raison, elle n'est foostockée que dans sys.tableset sys.objects, et rien ne permet de se plaindre que le schéma [dbo](avec les crochets) n'a pas été trouvé.

Mais cela se produit dans le code de CREATE TABLE. Ils utilisent peut-être PARSENAME(), comme l'a souligné David. Brancher le débogueur pourrait indiquer à coup sûr, mais est-ce important?

Vous pouvez regarder ailleurs pour voir ce qu'ils font, et sys.sp_renameen fait, cela donne ce qui PARSENAME()est utilisé:

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

Mais encore une fois, je ne suis pas sûr de comprendre pourquoi vous ne souhaitez supprimer que parfois les crochets.

Pour moi, personnellement, un pourcentage suffisamment important de mon code est écrit pour un public plus large, qui utilisera le code dans des environnements où je n'ai aucune connaissance ou contrôle s'ils utilisent des identifiants dangereux. J'ai donc toujours et j'écrirai (et je préfère) du code qui permet QUOTENAME()de générer des scripts qui incluent tout type d'identifiant.

Je préférerais de beaucoup avoir les crochets là tout le temps, plutôt que de les enlever et de me faire mordre la seule fois où ils étaient nécessaires.

Aaron Bertrand
la source
2
@McNets - Pour le nombre infime de cas où cela pourrait être utile, je préfère qu'ils se concentrent sur d'autres choses. Vous pouvez utiliser facilement les fonctions de chaîne existante pour dénuder avant et arrière [et ]et remplacer tout ]]avec]
Martin Smith
-6

Lorsque des crochets sont nécessaires - c'est parce que votre identifiant est déjà un mot-clé réservé. Les utiliser de façon arbitraire n'est pas seulement non nécessaire, cela conduit à beaucoup de confusion comme vous pouvez le voir.

À mon avis, la meilleure façon est d'éviter en premier lieu d'utiliser des identifiants réservés.

jinzai
la source