La codifica UCS-2 è sempre di 2 byte per carattere e ha un intervallo compreso tra 0 e 65535 (0x0000 - 0xFFFF). UTF-16 (indipendentemente da Big Endian o Little Endian) ha un intervallo compreso tra 0 e 1114111 (0x0000 - 0x10FFFF). L'intervallo 0 - 65535 / 0x0000 - 0xFFFF di UTF-16 è di 2 byte per carattere, mentre l'intervallo superiore a 65536 / 0xFFFF è di 4 byte per carattere.
Windows e SQL Server sono iniziati utilizzando la codifica UCS-2 perché era disponibile e UTF-16 non era ancora stato finalizzato. Fortunatamente, tuttavia, nei progetti di UCS-2 e UTF-16 è stato inserito un pensiero preliminare sufficiente per cui le mappature UCS-2 sono un sottoinsieme completo delle mappature UTF-16 (ovvero: la gamma 0 - 65535 / 0x0000 - 0xFFFF di UTF-16 è UCS-2). E, la gamma 65536-1114111 (0x10000 - 0x10FFFF) di UTF-16 è costruita da due punti di codice nell'intervallo UCS-2 (intervalli 0xD800 - 0xDBFF e 0xDC00 - 0xDFFF, in particolare) che erano riservati a questo scopo e altrimenti non hanno significato. Questa combinazione di due punti codice è nota come coppia surrogata e le coppie surrogate rappresentano personaggi oltre l'intervallo UCS-2, noti come caratteri supplementari.
Tutte queste informazioni spiegano due aspetti dei NVARCHAR
dati / Unicode in SQL Server:
- Diverse funzioni incorporate (non solo
NCHAR()
) non gestiscono coppie surrogate / caratteri supplementari quando non si utilizza una collazione a riconoscimento di carattere supplementare (SCA; ovvero una con _SC
, o _140_
non _BIN*
nel nome) perché le collazioni non SCA (in particolare il SQL_
Collation) sono stati originariamente implementati prima del completamento di UTF-16 (credo che nel 2000). Le non- SQL_
collation che hanno _90_
o _100_
nei loro nomi ma non _SC
hanno un supporto minimo per i personaggi supplementari in termini di confronto e ordinamento.
- Il set di caratteri Unicode / UTF-16 completo può essere memorizzato, senza alcuna perdita di dati, nei tipi di dati
NVARCHAR
/ NCHAR
/ XML
/ NTEXT
poiché UCS-2 e UTF-16 sono esattamente le stesse sequenze di byte. L'unica differenza è che UTF-16 utilizza i punti di codice surrogato per costruire coppie surrogate e UCS-2 semplicemente non può mapparli su alcun carattere, quindi appaiono alle funzioni integrate come due caratteri sconosciuti.
Con queste informazioni di base in mente, ora possiamo passare attraverso le domande specifiche:
Vorrei SELECT NCHAR(128512);
restituire lo stesso di questo:SELECT N'😀';
Ciò può accadere solo se il database corrente - in cui viene eseguita la query - ha un confronto predefinito che è a conoscenza del carattere supplementare e che sono stati introdotti in SQL Server 2012. Le funzioni incorporate con parametri di input stringa possono avere il confronto fornito in linea tramite la COLLATE
clausola (es. LEN(N'string' COLLATE Some_Collation_SC)
) e non è necessario eseguirla all'interno di un database con regole di confronto predefinite SCA. Tuttavia, funzioni integrate come NCHAR()
accettare un INT
parametro di input e la COLLATE
clausola non sono valide in quel contesto (motivo per cui NCHAR()
supporta solo caratteri supplementari quando il database corrente ha un confronto predefinito che è a conoscenza del carattere supplementare; ma questo non è necessario inconveniente che può essere modificato, quindi vota per il mio suggerimento:La funzione NCHAR () deve sempre restituire il carattere supplementare per i valori 0x10000 - 0x10FFFF indipendentemente dalle regole di confronto predefinite del database attivo ).
C'è una spiegazione del perché, indipendentemente dalle regole di confronto, SQL Server è in grado di comprendere e gestire i caratteri estesi se non dal punto di vista di NCHAR
?
Come SQL Server è in grado di archiviare e recuperare caratteri supplementari senza perdita di dati è stato spiegato nella parte superiore di questa risposta. Tuttavia, non è vero che NCHAR
è l'unica funzione integrata che presenta problemi con i caratteri supplementari (quando non si utilizza una raccolta SCA). Ad esempio, LEN(N'😀' COLLATE SQL_Latin1_General_CP1_CI_AS)
restituisce un valore di 2 mentre LEN(N'😀' COLLATE Latin1_General_100_CI_AS_SC)
restituisce un valore di 1.
Se vai al secondo link pubblicato nella domanda (ad esempio "Informazioni sulla raccolta dei caratteri supplementari di Microsoft") e scorri verso il basso solo un po ', vedrai un grafico delle funzioni integrate e di come si comportano in base all'efficace confronto.
Come trovo una collation che ha il flag "carattere supplementare"?
In una versione di SQL Server precedente al 2012 non è possibile. Tuttavia, a partire da SQL Server 2012, è possibile utilizzare la seguente query:
SELECT col.*
FROM sys.fn_helpcollations() col
WHERE col.[name] LIKE N'%[_]SC'
OR col.[name] LIKE N'%[_]SC[_]%'
OR (COLLATIONPROPERTY(col.[name], 'Version') = 3
AND col.[name] NOT LIKE N'%[_]BIN%');
La query è stata chiusa, ma il modello è iniziato con SQL
e le regole di confronto di SQL Server (ovvero quelle che iniziano con SQL_
) sono state deprecate per un po 'a favore delle regole di confronto di Windows (quelle che non iniziano con SQL_
). Pertanto, le SQL_
regole di confronto non vengono aggiornate e quindi non hanno versioni più recenti che includano l' _SC
opzione (e a partire da SQL Server 2017, tutte le nuove regole di confronto supportano automaticamente i caratteri supplementari e non hanno bisogno o hanno il _SC
flag; e sì, la query mostrato immediatamente sopra conti per questo e raccogliendo le _UTF8
regole di confronto aggiunte in SQL Server 2019).
È possibile installare regole di confronto su istanze precedenti?
No, non è possibile installare le regole di confronto in una versione precedente di SQL Server.
Come posso impostare una variabile stringa Unicode (ad es. Nvarchar) su un carattere supplementare usando il codice (senza usare il carattere supplementare effettivo) in un database in cui il confronto "non contiene il flag di carattere supplementare (SC)"?
...
Anche se il server è SQL Server 2008 R2, sono anche curioso di sapere qualsiasi soluzione per le versioni successive.
Quando non si utilizza un confronto SCA, è possibile iniettare punti di codice superiori a 65535 / U + FFFF in due modi:
- Specificare la coppia surrogata in termini di due chiamate alla
NCHAR()
funzione, ciascuna con una parte della coppia
- Specificare la coppia surrogata in termini di conversione della
VARBINARY
forma della sequenza di byte Little Endian (cioè invertita).
Questi due metodi per l'inserimento di caratteri supplementari / coppie surrogate funzioneranno anche se la raccolta effettiva è a conoscenza del carattere supplementare e dovrebbe funzionare allo stesso modo su tutte le versioni di SQL Server, almeno fino al 2005 (anche se probabilmente funzionerebbe anche in Anche SQL Server 2000).
Esempio:
- Personaggio:
💩
- Nome: mucchio di cacca
- Decimale: 128169
- Punto codice: U + 1F4A9
- Coppia surrogata: U + D83D e U + DF21
SELECT N'💩', -- 💩
UNICODE(N'💩' COLLATE Latin1_General_100_CI_AS), -- 55357
UNICODE(N'💩' COLLATE Latin1_General_100_CI_AS_SC), -- 128169
NCHAR(128169), -- 💩 in DB with _SC Collation, else NULL
NCHAR(0x1F4A9), -- 💩 in DB with _SC Collation, else NULL
CONVERT(VARBINARY(4), 128169), -- 0x0001F4A9
CONVERT(VARBINARY(4), N'💩'), -- 0x3DD8A9DC
CONVERT(NVARCHAR(10), 0x3DD8A9DC), -- 💩 (regardless of DB Collation)
NCHAR(0xD83D) + NCHAR(0xDCA9) -- 💩 (regardless of DB Collation)
AGGIORNARE
È possibile utilizzare il seguente iTVF per ottenere i valori della coppia surrogata (in entrambi INT
e in BINARY
forma) da qualsiasi punto di codice compreso tra 65536 e 1114111 (0x010000 - 0x10FFFF). E, mentre il parametro di input è di tipo INT
, è possibile passare nella forma binaria / esadecimale del punto di codice e verrà convertito implicitamente nel valore intero corretto.
CREATE FUNCTION dbo.GetSupplementaryCharacterInfo(@CodePoint INT)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
WITH calc AS
(
SELECT 55232 + (@CodePoint / 1024) AS [HighSurrogateINT],
56320 + (@CodePoint % 1024) AS [LowSurrogateINT]
WHERE @CodePoint BETWEEN 65536 AND 1114111
)
SELECT @CodePoint AS [CodePointINT],
HighSurrogateINT,
LowSurrogateINT,
CONVERT(VARBINARY(3), @CodePoint) AS [CodePointBIN],
CONVERT(BINARY(2), HighSurrogateINT) AS [HighSurrogateBIN],
CONVERT(BINARY(2), LowSurrogateINT) AS [LowSurrogateBIN],
CONVERT(binary(4), NCHAR(HighSurrogateINT) + NCHAR(LowSurrogateINT)) AS [UTF-16LE],
NCHAR(HighSurrogateINT) + NCHAR(LowSurrogateINT) AS [Character]
FROM calc;
GO
Utilizzando la funzione sopra, le seguenti due query:
SELECT * FROM dbo.GetSupplementaryCharacterInfo(128169);
SELECT * FROM dbo.GetSupplementaryCharacterInfo(0x01F4A9);
entrambi restituiscono quanto segue:
CodePoint HighSurrogate LowSurrgate CodePoint HighSurrgate LowSurrgate UTF-16LE Char
INT INT INT BIN BIN BIN actr
128169 55357 56489 0x01F4A9 0xD83D 0xDCA9 0x3DD8A9DC 💩
AGGIORNAMENTO 2: Un aggiornamento ancora migliore!
Ho adattato il iTVF mostrato sopra per ora restituire 188.657 punti di codice, quindi non è necessario adattarlo ad alcun valore particolare. Naturalmente, essendo un TVF, puoi aggiungere una WHERE
clausola per filtrare su un particolare punto di codice, o un intervallo di punti di codice, o "caratteri simili", ecc. E include colonne aggiuntive con sequenze di escape preformattate per costruire ogni codice punto (sia BMP che caratteri supplementari) in stile T-SQL, HTML e C (es \xHHHH
.). Leggi tutto qui:
Suggerimento SSMS n. 3: accedi / ricerca facilmente a TUTTI i personaggi Unicode (Sì, inclusi gli Emoji 😸)