Come posso impostare una stringa Unicode / NVARCHAR di SQL Server su un'emoji o un carattere supplementare?


23

Voglio impostare una variabile di stringa Unicode su un carattere particolare in base al suo punto di codice Unicode.

Voglio usare un punto di codice oltre 65535, ma il database SQL Server 2008 R2 ha un confronto di SQL_Latin1_General_CP1_CI_AS.

Secondo la documentazione NCHAR di Microsoft , ilNCHAR funzione accetta un numero intero come segue:

integer_expression

Quando le regole di confronto del database non contengono il flag di carattere supplementare (SC), si tratta di un numero intero positivo compreso tra 0 e 65535 (da 0 a 0xFFFF). Se viene specificato un valore al di fuori di questo intervallo, viene restituito NULL. Per ulteriori informazioni sui caratteri supplementari, consultare Collation and Unicode Support.

Quando le regole di confronto del database supportano il flag di carattere supplementare (SC), si tratta di un numero intero positivo compreso tra 0 e 1114111 (da 0 a 0x10FFFF). Se viene specificato un valore al di fuori di questo intervallo, viene restituito NULL.

Quindi questo codice:

SELECT NCHAR(128512);

ritorna NULL in questo database.

Vorrei che restituisse lo stesso di questo:

SELECT N'😀';

Come posso impostare una variabile stringa Unicode (ad es. Nvarchar) su un'emoji usando il codice (senza usare il carattere emoji effettivo) in un database in cui le regole di confronto "non contengono il flag del carattere supplementare (SC)"?

Elenco completo di punti di codice Unicode emoji

(Alla fine voglio che qualsiasi personaggio funzioni. Ho appena scelto le emoji per facilità di consultazione.)

(Anche se il server è SQL Server 2008 R2, sono anche curioso di sapere eventuali soluzioni per le versioni successive.)

Supponendo che non vi sia alcun modo, potrei fare riferimento a una funzione in linea definita dall'utente in un altro database che aveva un confronto appropriato?

Come trovo una collation che ha il flag "carattere supplementare"?

Questo non restituisce alcun record sul nostro server:

SELECT * FROM sys.fn_helpcollations() 
WHERE name LIKE 'SQL%[_]SC';

Sembra Latin1_General_100_CI_AS_SCche sia stato introdotto SQL Server 2012 che funzionerebbe. È possibile installare regole di confronto su istanze precedenti?

Riferimenti di confronto:

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?


Grazie per le informazioni aggiuntive complete. Non sto più affrontando questo problema, ma manterrò queste informazioni nei segnalibri mentali.
Riley Major,

1
Nessun problema. Non pensavo che avessi ancora bisogno di qualcosa, solo per poter apprezzare / essere in grado di sfruttare l'adattamento ...
Solomon Rutzky

Risposte:


36

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 NVARCHARdati / Unicode in SQL Server:

  1. 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 _SChanno un supporto minimo per i personaggi supplementari in termini di confronto e ordinamento.
  2. Il set di caratteri Unicode / UTF-16 completo può essere memorizzato, senza alcuna perdita di dati, nei tipi di dati NVARCHAR/ NCHAR/ XML/ NTEXTpoiché 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 COLLATEclausola (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 INTparametro di input e la COLLATEclausola 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 SQLe 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' _SCopzione (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 _SCflag; e sì, la query mostrato immediatamente sopra conti per questo e raccogliendo le _UTF8regole 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:

  1. Specificare la coppia surrogata in termini di due chiamate alla NCHAR()funzione, ciascuna con una parte della coppia
  2. Specificare la coppia surrogata in termini di conversione della VARBINARYforma 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 INTe in BINARYforma) 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 WHEREclausola 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 😸)


1
Ottimo lavoro Salomone! Spiegazione straordinaria
Ronen Ariely,
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.