SQL NVARCHAR- und VARCHAR-Grenzwerte


99

Insgesamt habe ich eine große (unvermeidbare) dynamische SQL-Abfrage. Aufgrund der Anzahl der Felder in den Auswahlkriterien wächst die Zeichenfolge mit dem dynamischen SQL um mehr als 4000 Zeichen. Jetzt verstehe ich, dass für maximal 4000 festgelegt ist NVARCHAR(MAX), aber wenn Sie sich die ausgeführte SQL in Server Profiler für die Anweisung ansehen

DELARE @SQL NVARCHAR(MAX);
SET @SQL = 'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO

Scheint zu funktionieren (!?). Bei einer anderen Abfrage, die ebenfalls groß ist, wird ein Fehler ausgegeben, der mit diesem 4000-Limit (!?) Verbunden ist. Im Grunde genommen wird die gesamte SQL nach diesem 4000-Limit gekürzt und es tritt ein Syntaxfehler auf. Trotzdem wird im Profiler diese dynamische SQL-Abfrage vollständig (!?) Angezeigt.

Was genau passiert hier und sollte ich diese @ SQL-Variable einfach in VARCHAR konvertieren und damit weitermachen?

Vielen Dank für Ihre Zeit.

Ps. Es wäre auch schön, mehr als 4000 Zeichen ausdrucken zu können, um diese großen Fragen zu beantworten. Die folgenden sind auf 4000 begrenzt

SELECT CONVERT(XML, @SQL);
PRINT(@SQL);

Gibt es einen anderen coolen Weg?


3
MAX ist kein Synonym für das 4000-Limit, sein 1..4000 oder MAX
Alex K.

Warum haben Sie die Frage mit C # DLL & Einstellung markiert, wenn dies nur eine SQL Server-Frage ist
HatSoft

Bearbeitet. Vielen Dank für das
Erkennen

PRINT verkettet mit 4000 Zeichen (für Unicode) oder 8000 Zeichen (für Einzelbyte-Codierungen). Ich vermute, das ist die Quelle der Verwirrung hier.
Redcalx

Antworten:


234

Ich verstehe, dass es ein Maximum von 4000 gibt NVARCHAR(MAX)

Dein Verständnis ist falsch. nvarchar(max)kann bis zu (und manchmal auch darüber hinaus) 2 GB Daten (1 Milliarde Doppelbyte-Zeichen) speichern.

Von nchar und nvarchar in Books online ist die Grammatik

nvarchar [ ( n | max ) ]

Der |Charakter bedeutet, dass dies Alternativen sind. dh Sie geben entweder n oder das Literal an max.

Wenn Sie sich für die Angabe eines bestimmten Objekts entscheiden, nmuss dieses zwischen 1 und 4.000 liegen. Bei Verwendung wird maxes jedoch als Datentyp für große Objekte definiert (dessen Ersatz ntextveraltet ist).

Tatsächlich scheint es in SQL Server 2008 so zu sein, dass für eine Variable das 2-GB-Limit auf unbestimmte Zeit überschritten werden kann, sofern genügend Speicherplatz in tempdb( hier gezeigt ) vorhanden ist.

In Bezug auf die anderen Teile Ihrer Frage

Das Abschneiden beim Verketten hängt vom Datentyp ab.

  1. varchar(n) + varchar(n) wird bei 8.000 Zeichen abgeschnitten.
  2. nvarchar(n) + nvarchar(n) wird bei 4.000 Zeichen abgeschnitten.
  3. varchar(n) + nvarchar(n)wird bei 4.000 Zeichen abgeschnitten. nvarcharhat eine höhere Priorität, so dass das Ergebnis istnvarchar(4,000)
  4. [n]varchar(max)+ [n]varchar(max)wird nicht abgeschnitten (für <2 GB).
  5. varchar(max)+ varchar(n)wird nicht abgeschnitten (für <2 GB) und das Ergebnis wird wie folgt eingegeben varchar(max).
  6. varchar(max)+ nvarchar(n)wird nicht abgeschnitten (für <2 GB) und das Ergebnis wird wie folgt eingegeben nvarchar(max).
  7. nvarchar(max)+ varchar(n)konvertiert zuerst die varchar(n)Eingabe in nvarchar(n)und führt dann die Verkettung durch. Wenn die Länge der varchar(n)Zeichenfolge größer als 4.000 Zeichen ist, erfolgt die nvarchar(4000)Umwandlung und es kommt zu einem Abschneiden .

Datentypen von String-Literalen

Wenn Sie die Verwendung NPräfix und die Zeichenfolge <= 4.000 Zeichen lange wird es als eingegeben werden , nvarchar(n)wo ndie Länge der Saite. So N'Foo'wird wie nvarchar(3)zum Beispiel behandelt . Wenn die Zeichenfolge länger als 4.000 Zeichen ist, wird sie als behandeltnvarchar(max)

Wenn Sie nicht über die Verwendung NPräfix und die Zeichenfolge <= 8.000 Zeichen lange wird es als eingegeben werden , varchar(n)wo ndie Länge der Saite. Wenn länger alsvarchar(max)

Wenn für beide oben genannten Punkte die Länge der Zeichenfolge Null nist, wird sie auf 1 gesetzt.

Neuere Syntaxelemente.

1. Die CONCATFunktion hilft hier nicht

DECLARE @A5000 VARCHAR(5000) = REPLICATE('A',5000);

SELECT DATALENGTH(@A5000 + @A5000), 
       DATALENGTH(CONCAT(@A5000,@A5000));

Das Obige gibt 8000 für beide Verkettungsmethoden zurück.

2. Seien Sie vorsichtig mit+=

DECLARE @A VARCHAR(MAX) = '';

SET @A+= REPLICATE('A',5000) + REPLICATE('A',5000)

DECLARE @B VARCHAR(MAX) = '';

SET @B = @B + REPLICATE('A',5000) + REPLICATE('A',5000)


SELECT DATALENGTH(@A), 
       DATALENGTH(@B);`

Kehrt zurück

-------------------- --------------------
8000                 10000

Beachten Sie, dass @Aeine Kürzung aufgetreten ist.

So lösen Sie das aufgetretene Problem.

Sie werden entweder abgeschnitten, weil Sie zwei Nicht- maxDatentypen miteinander verketten oder weil Sie eine varchar(4001 - 8000)Zeichenfolge mit einer nvarchartypisierten Zeichenfolge (gerade nvarchar(max)) verknüpfen .

Um das zweite Problem zu vermeiden, stellen Sie einfach sicher, dass alle Zeichenfolgenliterale (oder zumindest diejenigen mit Längen im Bereich von 4001 bis 8000) vorangestellt sind N.

Um das erste Problem zu vermeiden, ändern Sie die Zuordnung von

DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'Foo' + 'Bar' + ...;

Zu

DECLARE @SQL NVARCHAR(MAX) = ''; 
SET @SQL = @SQL + N'Foo' + N'Bar'

so dass ein NVARCHAR(MAX)von Anfang an an der Verkettung beteiligt ist (als Ergebnis jeder Verkettung wird sich NVARCHAR(MAX)dies auch ausbreiten)

Vermeiden von Kürzungen beim Anzeigen

Stellen Sie sicher, dass der Modus "Ergebnisse in Raster" ausgewählt ist, den Sie verwenden können

select @SQL as [processing-instruction(x)] FOR XML PATH 

Mit den SSMS-Optionen können Sie eine unbegrenzte Länge für XMLErgebnisse festlegen . Das processing-instructionBit vermeidet Probleme mit Zeichen wie <das Anzeigen als &lt;.


2
@ Killercam - Möglicherweise erhalten Sie nvarchar(4000)unterwegs eine implizite Besetzung . Wenn ein Zeichenfolgenliteral weniger als 4.000 Zeichen enthält, wird es als behandelt nvarchar(x). Wenn Sie einen anderen nvarchar(x)Wert damit verketten, wird dieser eher abgeschnitten als gesendetnvarchar(max)
Martin Smith

2
@ Killercam - Sie werden wahrscheinlich gemäß meinem ersten Kommentar abgeschnitten. Versuchen Sie, die Zuordnung DECLARE @SQL NVARCHAR(MAX) = ''; SET @SQL = @SQL + so zu ändern, dass NVARCHAR(MAX)an der Verkettung beteiligt ist.
Martin Smith

2
@ Killercam - Wahrscheinlich haben Sie eine Zeichenfolge zwischen 4.000 und 8.000 Zeichen. Mit dem NPräfix, das als nvarchar(max)ohne behandelt wird, wird es so behandelt, als würde es varchar(n)implizit umgewandelt, nvarchar(4000)wenn Sie sich mit einemnvarchar
Martin Smith

3
Ich bin durch diese Antwort erleuchtet
Mudassir Hasan

1
Tolle Antwort. Vielen Dank!
John Bell

6

Okay, wenn Sie später das Problem haben, dass Sie eine Abfrage haben, die größer als die zulässige Größe ist (was passieren kann, wenn sie weiter wächst), müssen Sie sie in Blöcke aufteilen und die Zeichenfolgenwerte ausführen. Angenommen, Sie haben eine gespeicherte Prozedur wie die folgende:

CREATE PROCEDURE ExecuteMyHugeQuery
    @SQL VARCHAR(MAX) -- 2GB size limit as stated by Martin Smith
AS
BEGIN
    -- Now, if the length is greater than some arbitrary value
    -- Let's say 2000 for this example
    -- Let's chunk it
    -- Let's also assume we won't allow anything larger than 8000 total
    DECLARE @len INT
    SELECT @len = LEN(@SQL)

    IF (@len > 8000)
    BEGIN
        RAISERROR ('The query cannot be larger than 8000 characters total.',
                   16,
                   1);
    END

    -- Let's declare our possible chunks
    DECLARE @Chunk1 VARCHAR(2000),
            @Chunk2 VARCHAR(2000),
            @Chunk3 VARCHAR(2000),
            @Chunk4 VARCHAR(2000)

    SELECT @Chunk1 = '',
           @Chunk2 = '',
           @Chunk3 = '',
           @Chunk4 = ''

    IF (@len > 2000)
    BEGIN
        -- Let's set the right chunks
        -- We already know we need two chunks so let's set the first
        SELECT @Chunk1 = SUBSTRING(@SQL, 1, 2000)

        -- Let's see if we need three chunks
        IF (@len > 4000)
        BEGIN
            SELECT @Chunk2 = SUBSTRING(@SQL, 2001, 2000)

            -- Let's see if we need four chunks
            IF (@len > 6000)
            BEGIN
                SELECT @Chunk3 = SUBSTRING(@SQL, 4001, 2000)
                SELECT @Chunk4 = SUBSTRING(@SQL, 6001, (@len - 6001))
            END
              ELSE
            BEGIN
                SELECT @Chunk3 = SUBSTRING(@SQL, 4001, (@len - 4001))
            END
        END
          ELSE
        BEGIN
            SELECT @Chunk2 = SUBSTRING(@SQL, 2001, (@len - 2001))
        END
    END

    -- Alright, now that we've broken it down, let's execute it
    EXEC (@Chunk1 + @Chunk2 + @Chunk3 + @Chunk4)
END

2

Sie müssen auch nvarchar-Text verwenden. Das heißt, Sie müssen einfach ein "N" vor Ihrer massiven Saite haben und das war's! Keine Einschränkung mehr

DELARE @SQL NVARCHAR(MAX);
SET @SQL = N'SomeMassiveString > 4000 chars...';
EXEC(@SQL);
GO

3
Dies ist nicht das gesamte Bild ... Wenn Sie das Präfix N verwenden und die Zeichenfolge <= 4.000 Zeichen lang ist, wird Folgendes eingegeben, nvarchar(n)wobei n die Länge der Zeichenfolge ist. So wird N'Foo 'wie nvarchar(3)zum Beispiel behandelt . Wenn die Zeichenfolge länger als 4.000 Zeichen ist, wird sie als behandelt nvarchar(max). Wenn Sie das Präfix N nicht verwenden und die Zeichenfolge <= 8.000 Zeichen lang ist, wird Folgendes eingegeben, varchar(n)wobei n die Länge der Zeichenfolge ist. Wenn länger als varchar(max). Wenn für beide oben genannten
Punkte

1

Die akzeptierte Antwort hat mir geholfen, aber ich bin beim Verketten von Varcharen mit Fallaussagen gestolpert. Ich weiß, dass die Frage des OP keine case-Anweisungen beinhaltet, aber ich dachte, dies wäre hilfreich, um sie hier für andere wie mich zu posten, die hier gelandet sind, während sie Schwierigkeiten hatten, lange dynamische SQL-Anweisungen mit case-Anweisungen zu erstellen.

Bei Verwendung von case-Anweisungen mit Zeichenfolgenverkettung gelten die in der akzeptierten Antwort genannten Regeln für jeden Abschnitt der case-Anweisung unabhängig.

declare @l_sql varchar(max) = ''

set @l_sql = @l_sql +
case when 1=1 then
    --without this correction the result is truncated
    --CONVERT(VARCHAR(MAX), '')
 +REPLICATE('1', 8000)
 +REPLICATE('1', 8000)
end

print len(@l_sql)

0
declare @p varbinary(max)
set @p = 0x
declare @local table (col text)

SELECT   @p = @p + 0x3B + CONVERT(varbinary(100), Email)
 FROM tbCarsList
 where email <> ''
 group by email
 order by email

 set @p = substring(@p, 2, 100000)

 insert @local values(cast(@p as varchar(max)))
 select DATALENGTH(col) as collen, col from @local

result collen > 8000, length col value is more than 8000 chars
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.