Bueno, los identificadores siempre son Unicode / NVARCHAR
, por lo que técnicamente no puedes crear nada que no tenga un nombre Unicode 🙃.
El problema que tiene aquí se debe completamente a la clasificación de los caracteres que se utilizan. Las reglas para los identificadores regulares (es decir, no delimitados) son:
- La primera letra debe ser:
- Una carta según la definición del estándar Unicode 3.2.
- guión bajo (_), en el signo (@) o signo de número (#)
- Las letras posteriores pueden ser:
- Letras tal como se definen en el Estándar Unicode 3.2.
- Números decimales del latín básico u otros scripts nacionales.
- subrayado (_), en el signo (@), signo de número (#) o signo de dólar ($)
- No se permiten espacios incrustados o caracteres especiales.
- No se permiten caracteres suplementarios.
En negrita las únicas reglas que importan en este contexto. La razón por la cual las reglas de "Primera letra" no son relevantes aquí es que la primera letra en todas las variables y parámetros locales es siempre el "signo de at" @
.
Y para ser claros: lo que se considera una "letra" y lo que se considera un "dígito decimal" se basa en las propiedades que cada carácter tiene asignado en la base de datos de caracteres Unicode. Unicode asigna muchas propiedades a cada carácter, como por ejemplo: is_uppercase, is_lowercase, is_digit, is_decimal, is_combining, etc., etc. Estas propiedades se usan a menudo en Expresiones regulares para que coincidan con la "puntuación", etc. Por ejemplo, \p{Lu}
coincide con cualquier letra mayúscula (en todos los idiomas / scripts) y \p{IsDingbats}
con cualquier carácter "Simbolos".
Entonces, en su intento de hacer:
DECLARE @¯\_(ツ)_/¯ INT;
solo los caracteres _
(subrayado o "línea baja") y ツ
(Katakana Letter Tu U + 30C4) se ajustan a esas reglas. Ahora, todos los caracteres ¯\_(ツ)_/¯
están bien para identificadores delimitados, pero desafortunadamente parece que los nombres y GOTO
etiquetas de variables / parámetros no pueden delimitarse (aunque los nombres de cursor sí pueden).
Por lo tanto, para los nombres de variables / parámetros, dado que no se pueden delimitar, debe usar solo caracteres que califiquen como "letras" o "dígitos decimales" a partir de Unicode 3.2 (bueno, de acuerdo con la documentación; necesito probar si las clasificaciones se han actualizado para las versiones más nuevas de Unicode, ya que las clasificaciones se manejan de manera diferente a los pesos de clasificación).
SIN EMBARGO # 1 , las cosas no son tan sencillas como deberían ser. Ahora he podido completar mi investigación y he descubierto que la definición establecida no es del todo correcta. La definición precisa (y verificable) de qué caracteres son válidos para los identificadores regulares es:
Primer personaje:
- Puede ser cualquier cosa clasificada en Unicode 3.2 como "ID_Start" (que incluye "Letras" pero también "caracteres numéricos tipo carta")
- Puede ser
_
(línea baja / subrayado) o _
(línea baja de ancho completo)
- Puede ser
@
, pero solo para variables / parámetros
- Puede ser
#
, pero si es un objeto vinculado al esquema, solo para tablas y procedimientos almacenados (en cuyo caso indican que el objeto es temporal)
Caracteres posteriores:
- Puede ser cualquier cosa clasificada en Unicode 3.2 como "ID_Continue" (que incluye números "decimales", pero también "signos de combinación de espaciado y no espaciado" y "conexión de signos de puntuación")
- Puede ser
@
, #
o$
- Puede ser cualquiera de los 26 caracteres clasificados en Unicode 3.2 como caracteres de control de formato
(Dato curioso: la "ID" en "ID_Start" y "ID_Continue" significa "Identificador". Imagínese eso ;-)
De acuerdo con "Utilidades Unicode: UnicodeSet":
Caracteres iniciales válidos
[: Edad = 3.2:] & [: ID_Start = Sí:]
-- Test one "Letter" from each of 10+ languages, as of Unicode 3.2
DECLARE @ᔠᑥᑒᏯשፙᇏᆇᄳᄈლဪඤaൌgೋӁウﺲﶨ INT;
-- works
-- Test a Supplementary Character that is a "Letter" as of Unicode 3.2
DECLARE @𝒲 INT;-- Mathematical Script Capital W (U+1D4B2)
/*
Msg 102, Level 15, State 1, Line XXXXX
Incorrect syntax near '0xd835'.
*/
Caracteres de continuación válidos
[: Age = 3.2:] & [: ID_Continue = Sí:]
-- Test various decimal numbers, but none are Supplementary Characters
DECLARE @६৮༦൯௫୫9 INT;
-- works (including some Hebrew and Arabic, which are right-to-left languages)
-- Test a Supplementary Character that is a "decimal" number as of Unicode 3.2
DECLARE @𝟜 INT; -- MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR (U+1D7DC)
/*
Msg 102, Level 15, State 1, Line XXXXX
Incorrect syntax near '0xd835'.
*/
-- D835 is the first character in the surrogate pair D835 DFDC that makes up U+1D7DC
SIN EMBARGO # 2 , ni siquiera buscar en la base de datos Unicode puede ser tan fácil. Esas dos búsquedas producen una lista de caracteres válidos para esas categorizaciones, y esos caracteres son de Unicode 3.2, PERO las definiciones de las diferentes categorizaciones cambian a través de las versiones del Estándar Unicode. Es decir, la definición de "ID_Start" en Unicode v 10.0 (lo que esa búsqueda está usando hoy, 2018-03-26) no es lo que era en Unicode v 3.2. Por lo tanto, la búsqueda en línea no puede proporcionar una lista exacta. Pero puede tomar los archivos de datos Unicode 3.2 y tomar la lista de caracteres "ID_Start" y "ID_Continue" desde allí para comparar con lo que SQL Server realmente usa. Y he hecho esto y he confirmado una coincidencia exacta con las reglas que dije anteriormente en "SIN EMBARGO # 1".
Las siguientes dos publicaciones de blog detallan los pasos dados para encontrar la lista exacta de caracteres, incluidos los enlaces a los scripts de importación:
- El Uni-Code: la búsqueda de la verdadera lista de caracteres válidos para identificadores regulares de T-SQL, Parte 1
- El Uni-Code: la búsqueda de la verdadera lista de caracteres válidos para identificadores regulares de T-SQL, Parte 2
Finalmente, para cualquiera que solo quiera ver la lista y no le preocupe lo que se necesitó para descubrirla y verificarla, puede encontrarla aquí:
Lista completamente completa de caracteres de identificación T-SQL válidos
(por favor, dedique un momento a la página para cargar; son 3,5 MB y casi 47k líneas)
Con respecto a los caracteres ASCII "válidos", como /
y -
, no funciona: el problema no tiene nada que ver con si los caracteres también están definidos en el conjunto de caracteres ASCII. Para que sea válido, el carácter debe tener la propiedad ID_Start
o la ID_Continue
propiedad, o ser uno de los pocos caracteres personalizados que se indican por separado. Hay bastantes caracteres ASCII "válidos" (62 del total de 128, principalmente caracteres de puntuación y control) que no son válidos en los identificadores "regulares".
Con respecto a los caracteres suplementarios: si bien ciertamente se pueden usar en identificadores delimitados (y la documentación no parece indicar lo contrario), si es cierto que no se pueden usar en identificadores regulares, probablemente se deba a que no son totalmente compatibles en las funciones integradas anteriores a las intercalaciones suplementarias con reconocimiento de caracteres se introdujeron en SQL Server 2012 (se tratan como dos caracteres "desconocidos" individuales), ni siquiera podrían diferenciarse entre sí en intercalaciones no binarias anteriores a la 100- Intercalaciones de nivel (introducidas en SQL Server 2008).
Con respecto a ASCII: las codificaciones de 8 bits no se utilizan aquí ya que todos los identificadores son Unicode / NVARCHAR
/ UTF-16 LE. La declaración SELECT ASCII('ツ');
devuelve un valor 63
cuyo es un "?" (intente:) SELECT CHAR(63);
ya que ese carácter, incluso si está prefijado con una "N" mayúscula, ciertamente no está en la página de códigos 1252. Sin embargo, ese carácter está en la página de códigos coreana y produce el resultado correcto, incluso sin la "N "prefijo, en una base de datos con una clasificación coreana predeterminada:
SELECT UNICODE('ツ'); -- 12484
Con respecto a la primera letra que afecta el resultado: esto no es posible ya que la primera letra para variables y parámetros locales es siempre @
. La primera letra que podemos controlar para estos nombres es en realidad el segundo carácter del nombre.
Con respecto a por qué los nombres de variables locales, los nombres de parámetros y las GOTO
etiquetas no se pueden delimitar: sospecho que esto se debe a que estos elementos son parte del lenguaje en sí y no algo que se encontrará en una tabla del sistema como datos.