Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Supporto UTF-8, SQL Server 2012 e UDT UTF8String

La creazione di un tipo personalizzato definito dall'utente tramite SQLCLR non , in ogni caso, ti procurerà un sostituto di qualsiasi tipo nativo. È molto utile per creare qualcosa per gestire dati specializzati. Ma le stringhe, anche con una codifica diversa, sono tutt'altro che specializzate. Seguire questa strada per i tuoi dati di stringa distruggerebbe qualsiasi quantità di usabilità del tuo sistema, per non parlare delle prestazioni poiché non saresti in grado di usarne nessuna funzioni di stringa integrate.

Se fossi in grado di salvare qualcosa sullo spazio su disco, quei guadagni verrebbero cancellati da ciò che perderesti in termini di prestazioni complessive. La memorizzazione di un UDT avviene serializzandolo in un VARBINARY . Quindi per fare qualsiasi confronto di stringhe O ordinamento, al di fuori di un confronto "binario" / "ordinale", dovresti convertire tutti gli altri valori, uno per uno, di nuovo in UTF-8 per quindi eseguire il confronto di stringhe che può tenere conto delle differenze linguistiche. E quella conversione dovrebbe essere eseguita all'interno dell'UDT. Ciò significa che, come il tipo di dati XML, dovresti creare l'UDT per contenere un valore particolare, quindi esporre un metodo di tale UDT per accettare un parametro stringa per eseguire il confronto (ad esempio Utf8String.Compare(alias.field1) oppure, se si definisce un operatore per il tipo, allora Utf8string1 = Utf8string2 e avere il = l'operatore ottiene la stringa nella codifica UTF-8 e quindi esegue CompareInfo.Compare() ).

Oltre alle considerazioni precedenti, devi anche considerare che il passaggio di valori avanti e indietro tramite l'API SQLCLR ha un costo, soprattutto quando si utilizza NVARCHAR(MAX) o VARBINARY(MAX) al contrario di NVARCHAR(1 - 4000) e VARBINARY(1 - 4000) rispettivamente (per favore non confondere questa distinzione con il fatto che implica qualcosa sull'utilizzo di SqlChars / SqlBytes rispetto a SqlString / SqlBinary ).

Infine (almeno in termini di utilizzo di un UDT), per favore non guardare oltre il fatto che l'UDT di cui si chiede informazioni è codice di esempio . L'unico test notato è puramente funzionale, niente sulla scalabilità o sulle "lezioni apprese dopo aver lavorato con questo per un anno". Il codice del test funzionale è mostrato qui nella seguente pagina CodePlex e dovrebbe essere esaminato prima di procedere con questa decisione in quanto dà un'idea di come avresti bisogno di scrivere le tue query per interagire con esso (che va bene per un campo o due, ma non per la maggior parte/tutti i campi stringa):

http://msftengprodsamples.codeplex.com /SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql

Dato il numero di colonne calcolate persistenti e indici aggiunti, è stato davvero risparmiato spazio?;-)

Laddove lo spazio (disco, memoria, ecc.) è il problema, hai tre opzioni:

  1. Se utilizzi SQL Server 2008 o versioni successive e sei in Enterprise Edition, puoi abilitare Compressione dati . La compressione dei dati può (ma non "sempre") comprimere i dati Unicode in NCHAR e NVARCHAR campi. I fattori determinanti sono:

    1. NCHAR(1 - 4000) e NVARCHAR(1 - 4000) utilizzare lo Schema di compressione standard per Unicode , ma solo a partire da SQL Server 2008 R2 E solo per i dati IN ROW, non OVERFLOW! Questo sembra essere migliore del normale algoritmo di compressione ROW/PAGE.
    2. NVARCHAR(MAX) e XML (e suppongo anche VARBINARY(MAX) , TEXT e NTEXT ) i dati IN ROW (non fuori riga nelle pagine LOB o OVERFLOW) possono essere compressi almeno PAGE e forse anche ROW compresso (non sono sicuro di quest'ultimo).
    3. Qualsiasi dato OFF ROW, LOB o OVERLOW =Nessuna compressione per te!
  2. Se utilizzi una versione precedente al 2008 o meno su Enterprise Edition, puoi avere due campi:uno VARCHAR e un NVARCHAR . Ad esempio, supponiamo che tu stia memorizzando URL che sono per lo più tutti caratteri ASCII di base (valori 0 - 127) e quindi rientrano in VARCHAR , ma a volte hanno caratteri Unicode. Il tuo schema può includere i seguenti 3 campi:

      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );
    

    In questo modello tu solo SELEZIONA dal [URL] colonna calcolata. Per l'inserimento e l'aggiornamento, si determina quale campo utilizzare vedendo se la conversione altera il valore in entrata, che deve essere di NVARCHAR digita:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. Se hai campi che dovrebbero contenere solo caratteri che si adattano a una particolare tabella codici di un set di caratteri ASCII esteso, usa semplicemente VARCHAR .

PS Giusto per avere questo detto per chiarezza:il nuovo _SC Le regole di confronto introdotte in SQL Server 2012 consentono semplicemente:

  • le funzioni integrate per gestire correttamente i caratteri supplementari / le coppie surrogate e
  • regole linguistiche per i caratteri supplementari utilizzati per l'ordinamento e il confronto

Ma, anche senza il nuovo _SC Fascicolazioni, puoi comunque archiviare qualsiasi carattere Unicode in un XML o N -tipo con prefisso e recuperalo senza perdita di dati. Tuttavia, quando si utilizzano le regole di confronto precedenti (ovvero nessun numero di versione nel nome), tutti i caratteri supplementari sono uguali tra loro. Devi usare il _90 e _100 Fascicolazioni che ti consentono almeno di confrontare e ordinare i punti di codice/binario; non possono tenere conto delle regole linguistiche poiché non hanno mappature particolari dei Caratteri Supplementari (e quindi non hanno pesi o regole di normalizzazione).

Prova quanto segue:

IF (N'𤪆' = N'𤪆') SELECT N'𤪆' AS [TheLiteral], NCHAR(150150) AS [Generated];
IF (N'𤪆' = N'𤪇') SELECT N'𤪇' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'𤪆' COLLATE Tatar_90_CI_AI = N'𤪇' COLLATE Tatar_90_CI_AI)
       SELECT N'𤪇 COLLATE Tatar_90_CI_AI' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'𤪆' = N'?') SELECT N'?';

In un DB con regole di confronto predefinite che terminano con _SC , solo il primo IF l'istruzione restituirà un set di risultati e il campo "Generato" mostrerà i caratteri correttamente.

Tuttavia, se il DB non ha regole di confronto predefinite che terminano con _SC e le regole di confronto non sono un _90 o _100 serie di confronto, quindi i primi due IF le istruzioni restituiscono set di risultati in cui il campo "Generato" restituirà NULL e il campo "Letterale" viene visualizzato correttamente.

Per i dati Unicode, le regole di confronto non hanno alcun effetto sull'archiviazione fisica.

AGGIORNAMENTO 2018-10-02

Sebbene questa non sia ancora un'opzione praticabile, SQL Server 2019 introduce il supporto nativo per UTF-8 in VARCHAR / CHAR tipi di dati. Al momento ci sono troppi bug con esso per poter essere utilizzato, ma se vengono risolti, questa è un'opzione per alcuni scenari. Si prega di consultare il mio post, "Supporto UTF-8 nativo in SQL Server 2019:Salvatore o Falso profeta? ", per un'analisi dettagliata di questa nuova funzionalità.