¿Hay alguna función incorporada (oculta) en MS-SQL para poner entre comillas los nombres de los objetos?

12

A veces almaceno nombres de objetos (identificadores) en algunas de nuestras bases de datos, por ejemplo, en algunas tablas de parámetros. Debido a que selecciono registros de estas tablas usando los operadores de comparación '=' o 'LIKE', debo tener cuidado de almacenar estos nombres siempre con o sin corchetes .

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

o

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

Sin embargo, MS-SQL tiene algunas funciones donde puede usar nombres de objetos con o sin corchetes, por ejemplo, la función OBJECT_ID (). He configurado un ejemplo mínimo en 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

Ahora puedo usar OBJECT_ID () para verificar si la tabla TEST existe de esta manera:

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

No importa si paso el TEST de identificador con o sin paréntesis, el analizador es lo suficientemente inteligente como para eliminar los paréntesis.

Bueno, puedo simular esto agregando una función escalar que elimine los corchetes de una cadena:

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

Y luego úsalo de esta manera:

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  

Pero mi pregunta es:

  • ¿Hay alguna función incorporada oculta que elimine los corchetes usando T-SQL?

dbfiddle aquí

McNets
fuente

Respuestas:

12

¿Hay alguna función incorporada oculta que elimine los corchetes usando T-SQL?

No , no estoy usando T-SQL.

OBJECT_IDEs una función intrínseca . Se implementa directamente en el código ejecutable de SQL Server, no en T-SQL; y no llama a ningún T-SQL cuando se invoca.

En tiempo de ejecución, la identificación del objeto se obtiene mediante la llamada al servicio de expresión sqlmin!I4ObjIdWstr.

Luego, la implementación realiza todos los pasos necesarios para resolver los parámetros de cadena proporcionados en la identificación de un objeto en la base de datos referenciada.

Uno de los primeros pasos incluye tratar con cualquier identificador delimitado en la cadena a través de sqlmin!CbParseQuotesW. En un sentido estricto, esa es la función de código a la que se refiere, pero no es accesible directamente desde T-SQL. Contiene el siguiente 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 son pruebas para manejar los caracteres:

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

El resto del proceso para resolver los parámetros de una identificación implica:

  • Iniciar una transacción automática de solo lectura
  • Comprobación de requisitos de base de datos contenidos
  • Iterando a través de posibles coincidencias para el parámetro de nombre (usando la clasificación correcta)
    • En el nombre de la base de datos suministrada (o la base de datos de contexto actual)
    • En el nombre del esquema proporcionado (o sys , o el esquema predeterminado del usuario, etc.)
  • Tomar los bloqueos de metadatos necesarios
  • Consultar el caché de metadatos para una coincidencia
  • Obteniendo metadatos en caché si es necesario
  • Comprobación de permisos (para acceder a la identificación del objeto)
  • Devolver la identificación del primer objeto coincidente (si lo hay)

En una nota al margen, el código en la pregunta:

IF OBJECT_ID('TEST') IS NOT NULL

... no busca solo tablas. Para eso requeriría usar el segundo parámetro de función. Además, solo busca cualquier objeto de ámbito de esquema llamado TEST, por lo que una vista llamada BananaSchema.TEST coincidiría, por ejemplo. Una mejor expresión sería:

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

Preguntas y respuestas relacionadas:

Paul White 9
fuente
18

A veces almaceno nombres de objetos en algunas de nuestras bases de datos

Debo tener cuidado de almacenar estos nombres siempre con o sin corchetes.

Un "nombre de objeto" se denomina técnicamente un identificador . En algunos contextos aparecerá un identificador de código TSQL rodeado por [y] o "y". Estos caracteres no son parte del identificador, y nunca debe almacenarlos.

En su lugar, almacene el identificador como nvarchar (128) (o sysname) y agregue los delimitadores en tiempo de ejecución utilizando la función QUOTENAME .

El inverso de QUOTENAME es PARSENAME , que tiene la capacidad adicional de navegar por nombres de varias partes.

Tenga en cuenta que QUOTENAME tiene un segundo parámetro opcional, y si especifica un carácter de comilla simple para ese parámetro, QUOTENAME no crea una expresión de identificador delimitada válida. Emite una expresión literal varchar.

David Browne - Microsoft
fuente
7

SQL Server obviamente tiene algo interno que elimina [square brackets](u otros identificadores, como "double quotes").

Cuando crea una tabla como [dbo].[foo], tiene razón, solo foose almacena en sys.tablesy sys.objects, y no se queja de que no se encontró el esquema [dbo](con corchetes).

Pero esto sucede dentro del código para CREATE TABLE. Pueden estar usando PARSENAME(), como señaló David. Conectar el depurador podría indicarlo con seguridad, pero ¿importa?

Puede buscar en otro lado para ver lo que están haciendo, y sys.sp_renamede hecho produce lo que PARSENAME()se usa:

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

Pero, una vez más, no estoy seguro de entender por qué solo quieres eliminar a veces los corchetes.

Para mí, personalmente, un porcentaje suficientemente grande de mi código está escrito para una audiencia más amplia, que usará el código en entornos donde no tengo conocimiento o control sobre si están utilizando identificadores inseguros. Por lo tanto, siempre tengo y siempre escribiré (y preferiré) el código que se usa QUOTENAME()para generar scripts que incluyen cualquier tipo de identificador.

Preferiría tener los corchetes allí todo el tiempo, que quitarlos y ser mordidos la única vez que fueron necesarios.

Aaron Bertrand
fuente
2
@McNets: para la minúscula cantidad de casos en los que esto podría ser útil, preferiría que se centraran en otras cosas. Se puede utilizar fácilmente las funciones de cadena existente para despojarse de ataque y de salida [y ]y reemplazar cualquier ]]con]
Martin Smith
-6

Cuando se necesitan corchetes , es porque su identificador ya es una palabra clave reservada. Usarlos arbitrariamente no solo no es necesario, sino que genera mucha confusión, como puede ver.

En mi opinión, la mejor manera es evitar el uso de identificadores reservados en primer lugar.

jinzai
fuente