SQL NVARCHAR 및 VARCHAR 제한


100

모두, 큰 (피할 수없는) 동적 SQL 쿼리가 있습니다. 선택 기준의 필드 수로 인해 동적 SQL을 포함하는 문자열이 4000 자 이상 증가합니다. 이제에는 4000 max가 설정되어 NVARCHAR(MAX)있지만 Server Profiler에서 실행 된 SQL을보고

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

작동하는 것 같습니다 (!?), 또한 큰 다른 쿼리의 경우이 4000 제한 (!?)과 관련된 오류가 발생하며 기본적 으로이 4000 제한 이후의 모든 SQL을 자르고 구문 오류가 발생합니다. 프로파일 러에서는 이에도 불구하고이 동적 SQL 쿼리를 전체 (!?)로 표시합니다.

여기서 정확히 무슨 일이 일어나고 있으며이 @SQL 변수를 VARCHAR로 변환하고 계속해야합니까?

시간 내 줘서 고마워.

추신. 이러한 큰 쿼리를보기 위해 4000 자 이상을 인쇄 할 수 있으면 좋을 것입니다. 다음은 4000 개로 제한됩니다.

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

다른 멋진 방법이 있습니까?


3
MAX는 4000 제한, 1..4000 또는 MAX
Alex K

왜 당신이 질문에 C # dll 및 설정 s when this is just a Sql Server question
HatSoft

편집. 찾아 주셔서 감사합니다 ...
MoonKnight

PRINT는 4000 자 (유니 코드의 경우) 또는 8000 자 (단일 바이트 인코딩의 경우)로 연결됩니다. 나는 그것이 여기서 혼란의 원인이라고 생각합니다.
redcalx 2013-06-13

답변:


235

나는 4000 최대 세트가 있음을 이해합니다. NVARCHAR(MAX)

이해가 잘못되었습니다. nvarchar(max)최대 (때로는 그 이상) 2GB의 데이터 (10 억 2 바이트 문자)를 저장할 수 있습니다.

에서 NCHAR 및 NVARCHAR 책 온라인 문법이다

nvarchar [ ( n | max ) ]

|문자 수단이 대안이다. 즉 , n 또는 리터럴 을 지정 합니다max .

특정을 지정하도록 선택하는 경우 n1에서 4,000 사이 여야하지만 using을 사용 max하면이를 대형 개체 데이터 유형으로 정의합니다 (대체 ntext가 더 이상 사용되지 않음).

실제로 SQL Server 2008에서는 변수의 경우 2GB 제한이 tempdb( 여기에 표시됨 )의 충분한 공간에 따라 무기한 초과 될 수 있습니다.

질문의 다른 부분에 관해서

연결시 잘림은 데이터 유형에 따라 다릅니다.

  1. varchar(n) + varchar(n) 8,000 자에서 잘립니다.
  2. nvarchar(n) + nvarchar(n) 4,000 자에서 잘립니다.
  3. varchar(n) + nvarchar(n)4,000 자에서 잘립니다. nvarchar우선 순위가 높으므로 결과는nvarchar(4,000)
  4. [n]varchar(max)+ [n]varchar(max)는 잘리지 않습니다 (2GB 미만).
  5. varchar(max)+ varchar(n)는 잘리지 않으며 (2GB 미만) 결과는 varchar(max).
  6. varchar(max)+ nvarchar(n)는 잘리지 않으며 (2GB 미만) 결과는 nvarchar(max).
  7. nvarchar(max)+ varchar(n)는 먼저 varchar(n)입력을 로 변환 nvarchar(n)한 다음 연결을 수행합니다. 의 길이 경우 varchar(n)문자열보다 큰 4,000 자 캐스트에있을 것입니다 nvarchar(4000)및 절단이 발생합니다 .

문자열 리터럴의 데이터 유형

N접두사 를 사용하고 문자열 길이가 <= 4,000자인 경우 문자열 길이가 nvarchar(n)어디에있는 것처럼 입력됩니다 n. 그래서 예를 들어 N'Foo'취급됩니다 nvarchar(3). 문자열이 4,000 자보다 길면 다음과 같이 처리됩니다.nvarchar(max)

N접두사를 사용하지 않고 문자열 길이가 <= 8,000자인 경우 문자열 길이가 varchar(n)어디에있는 것처럼 입력됩니다 n. 더 긴 경우varchar(max)

위의 두 경우 모두 문자열 길이가 0이면 n1로 설정됩니다.

최신 구문 요소.

1.CONCAT기능은 여기서 도움이되지 않습니다.

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

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

위의 두 연결 방법에 대해 8000을 반환합니다.

2. 조심하세요+=

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

보고

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

@A잘림 이 발생했습니다.

발생한 문제를 해결하는 방법.

두 개의 비 max데이터 유형을 함께 연결하거나 varchar(4001 - 8000)문자열을 nvarchar유형이 지정된 문자열 (심지어 nvarchar(max))에 연결하기 때문에 잘림이 발생합니다 .

두 번째 문제를 방지하려면 모든 문자열 리터럴 (또는 최소한 4001-8000 범위의 길이를 가진 문자열) 앞에 N.

첫 번째 문제를 방지하려면 다음에서 할당을 변경하십시오.

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

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

그래서 an NVARCHAR(MAX)이 처음부터 연결에 포함 되도록 (각 연결의 결과로 NVARCHAR(MAX)전파 될 것입니다)

볼 때 잘림 방지

"그리드 결과"모드를 선택했는지 확인한 다음 사용할 수 있습니다.

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

SSMS 옵션을 사용하면 XML결과 길이를 무제한으로 설정할 수 있습니다 . processing-instruction비트는 다음과 같은 문자가 문제 방지 <로 게재를 &lt;.


2
@Killercam-당신은 nvarchar(4000)도중에 암시적인 캐스트를받을 수 있습니다 . 문자열 리터럴이 4,000 자 미만이면 nvarchar(x). 다른 여기에 합치 nvarchar(x)값으로 업 캐스팅보다는 자릅니다nvarchar(max)
마틴 스미스

2
@Killercam-내 첫 번째 댓글에 따라 잘림이 발생했을 것입니다. DECLARE @SQL NVARCHAR(MAX) = ''; SET @SQL = @SQL + an NVARCHAR(MAX)이 연결에 포함 되도록 할당을로 변경해보십시오 .
Martin Smith

2
@Killercam-아마도 4,000에서 8,000 자 사이의 문자열이있을 것입니다. 와 N같이 처리됩니다 접두사 nvarchar(max)가로 처리됩니다 그것을하지 않고 varchar(n)암시 적으로 캐스팅 nvarchar(4000)당신이에 연결할 때nvarchar
마틴 스미스

3
나는이 답변에 의해 계몽하고
Mudassir 하산을

1
멋진 대답입니다. 정말 고마워!
John Bell

6

좋아, 그래서 나중에 문제가 허용 가능한 크기보다 큰 쿼리가 있다는 것이라면 (계속 커질 경우 발생할 수 있음) 쿼리를 덩어리로 나누고 문자열 값을 실행해야합니다. 따라서 다음과 같은 저장 프로 시저가 있다고 가정 해 보겠습니다.

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

nvarchar 텍스트도 사용합니다. 그것은 당신이 당신의 거대한 문자열 앞에 "N"을 가지고 있어야한다는 것을 의미합니다. 그리고 그게 다입니다! 더 이상 제한 없음

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

3
이것은 전체 그림이 아닙니다 ... N 접두사를 사용하고 문자열 길이가 <= 4,000 자이면 nvarchar(n)n은 문자열 길이 로 입력됩니다 . 따라서 N'Foo '는 nvarchar(3)예를 들어 취급됩니다 . 문자열이 4,000 자보다 길면 nvarchar(max). N 접두사를 사용하지 않고 문자열 길이가 <= 8,000자인 varchar(n)경우 n은 문자열 길이 로 입력됩니다 . 더 긴 경우 varchar(max). 위의 두 가지 모두 문자열의 길이가 0이면 n은 1로 설정됩니다.
MoonKnight

1

받아 들여진 대답은 나를 도왔지만 case 문과 관련된 varchar를 연결하는 동안 넘어졌습니다. 나는 OP의 질문에 case 문이 포함되어 있지 않다는 것을 알고 있지만 case 문을 포함하는 긴 동적 SQL 문을 작성하는 데 어려움을 겪으면서 여기에 온 나와 같은 다른 사람들을 위해 여기에 게시하는 것이 도움이 될 것이라고 생각했습니다.

문자열 연결과 함께 case 문을 사용할 때 수락 된 답변에 언급 된 규칙이 case 문의 각 섹션에 독립적으로 적용됩니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.