sql >> Databasteknik >  >> RDS >> Sqlserver

UTF-8 Support, SQL Server 2012 och UTF8String UDT

Att skapa en anpassad användardefinierad typ via SQLCLR är inte , på vilket sätt som helst, kommer att ge dig en ersättning av vilken typ som helst. Det är väldigt praktiskt för att skapa något för att hantera specialiserad data. Men strängar, även av en annan kodning, är långt ifrån specialiserade. Att gå den här vägen för dina strängdata skulle förstöra all användbarhet av ditt system, för att inte tala om prestanda eftersom du inte skulle kunna använda någon inbyggda strängfunktioner.

Om du kunde spara vad som helst på diskutrymme, skulle dessa vinster raderas av vad du skulle förlora i total prestanda. Att lagra en UDT görs genom att serialisera den till en VARBINARY . Så för att göra vilket som helst strängjämförelse ELLER sortering, utanför en "binär" / "ordinär" jämförelse, skulle du behöva konvertera alla andra värden, ett efter ett, tillbaka till UTF-8 för att sedan göra strängjämförelsen som kan ta hänsyn till språkliga skillnader. Och den konverteringen skulle behöva göras inom UDT. Detta innebär att du, precis som XML-datatypen, skulle skapa UDT för att hålla ett visst värde och sedan exponera en metod för den UDT för att acceptera en strängparameter för att göra jämförelsen (dvs. Utf8String.Compare(alias.field1) eller, om du definierar en operator för typen, då Utf8string1 = Utf8string2 och har = operatorn hämta strängen i UTF-8-kodningen och gör sedan CompareInfo.Compare() ).

Utöver ovanstående överväganden måste du också tänka på att det kostar en kostnad att skicka värden fram och tillbaka genom SQLCLR API, särskilt när du använder antingen NVARCHAR(MAX) eller VARBINARY(MAX) i motsats till NVARCHAR(1 - 4000) och VARBINARY(1 - 4000) respektive (förväxla inte denna distinktion som att den antyder något om att använda SqlChars / SqlBytes kontra SqlString / SqlBinary ).

Slutligen (åtminstone när det gäller att använda en UDT), vänligen se inte förbi det faktum att UDT som frågas om är exempelkod . Den enda testningen som noteras är rent funktionell, ingenting kring skalbarhet eller "lärdomar efter att ha arbetat med detta i ett år". Den funktionella testkoden visas här på följande CodePlex-sida och bör tittas på innan du fortsätter med detta beslut eftersom den ger en känsla av hur du skulle behöva skriva dina frågor för att interagera med den (vilket är bra för ett fält eller två, men inte för de flesta / alla strängfält):

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

Med tanke på antalet beständiga beräknade kolumner och index som lagts till, sparades verkligen något utrymme?;-)

Där utrymme (disk, minne, etc) är problemet, har du tre alternativ:

  1. Om du använder SQL Server 2008 eller senare och använder Enterprise Edition kan du aktivera Datakomprimering . Datakomprimering kan (men kommer inte "alltid") komprimera Unicode-data i NCHAR och NVARCHAR fält. De avgörande faktorerna är:

    1. NCHAR(1 - 4000) och NVARCHAR(1 - 4000) använd Standard Compression Scheme för Unicode , men endast med start i SQL Server 2008 R2, OCH endast för IN ROW-data, inte OVERFLOW! Detta verkar vara bättre än den vanliga ROW/PAGE-komprimeringsalgoritmen.
    2. NVARCHAR(MAX) och XML (och jag antar att även VARBINARY(MAX) , TEXT och NTEXT ) data som är PÅ RAD (inte utanför raden på LOB- eller OVERFLOW-sidor) kan åtminstone PAGE-komprimeras, och kanske även ROW-komprimerad (osäker på den sista).
    3. Alla OFF ROW-data, LOB eller OVERLOW =Ingen komprimering för dig!
  2. Om du använder en version som är äldre än 2008 eller inte på Enterprise Edition kan du ha två fält:ett VARCHAR och en NVARCHAR . Låt oss till exempel säga att du lagrar webbadresser som oftast består av bas-ASCII-tecken (värden 0 - 127) och därför passar in i VARCHAR , men har ibland Unicode-tecken. Ditt schema kan innehålla följande tre fält:

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

    I den här modellen du endast VÄLJ från [URL] beräknad kolumn. För att infoga och uppdatera bestämmer du vilket fält som ska användas genom att se om konvertering ändrar det inkommande värdet, som måste vara NVARCHAR typ:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. Om du har fält som bara ska ha tecken som passar in i en viss kodsida i en utökad ASCII-teckenuppsättning, använd bara VARCHAR .

P.S. Bara för att få det här angivet för tydlighetens skull:den nya _SC Sorteringar som introducerades i SQL Server 2012 tillåter helt enkelt:

  • de inbyggda funktionerna för att korrekt hantera tilläggstecken / surrogatpar, och
  • språkliga regler för tilläggstecken som används för beställning och jämförelser

Men även utan den nya _SC Sorteringar, du kan fortfarande lagra vilket Unicode-tecken som helst i en XML eller N -prefix, och hämta den utan dataförlust. Men när du använder de äldre sorteringarna (dvs. inget versionsnummer i namnet) är alla tilläggstecken lika med varandra. Du måste använda _90 och _100 Kollationer som åtminstone ger dig binära / kodpunktsjämförelser och sortering; de kan inte ta hänsyn till språkliga regler eftersom de inte har några speciella mappningar av de kompletterande tecknen (och därför inte har några vikter eller normaliseringsregler).

Prova följande:

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'?';

I en DB som har en standardsortering som slutar på _SC , bara den första IF -satsen returnerar en resultatuppsättning och fältet "Genererat" visar tecknen korrekt.

Men om DB:n inte har en standardsortering som slutar på _SC , och sorteringen är inte en _90 eller _100 seriekollation, sedan de två första IF satser returnerar resultatuppsättningar där fältet "Genererat" returnerar NULL , och fältet "Literal" visas korrekt.

För Unicode-data har sorteringen ingen betydelse för fysisk lagring.

UPPDATERING 2018-10-02

Även om detta inte är ett genomförbart alternativ än, introducerar SQL Server 2019 inbyggt stöd för UTF-8 i VARCHAR / CHAR datatyper. Det finns för närvarande för många buggar med den för att den ska kunna användas, men om de är åtgärdade är detta ett alternativ för vissa scenarier. Se mitt inlägg, "Native UTF-8-stöd i SQL Server 2019:Frälsare eller False Prophet? ", för en detaljerad analys av denna nya funktion.




  1. SQL Server Automatiserade säkerhetskopior

  2. php-begränsningsöverträdelse Fel 1452

  3. Hur kontrollerar jag mina InnoDB-inställningar?

  4. PHP MySQL visar flera rader grupperade efter vanliga fält