声明为Varchar8000的SQL变量在5160处修剪内容

问题描述

我已声明sql1数据类型为varchar(max)的变量,最大长度为8000。我期望它最多容纳8000个字符,但是在我的情况下,即使字符串长度为5160,该变量仍然不能容纳整个查询

DECLARE @sql1 VARCHAR (8000)
--DECLARE @sql2 VARCHAR (8000)
SET @sql1=''
SET @sql1= 
'
UPDATE v
SET v.ValueLoc = (sf.ValueLoc*isnull(sf.FTE,1)*-1),v.FTE = sf.FTE,v.CurrencyLOC = sf.Currency,v.K4_MODIFIED = getDate()
FROM ##STAFF_PLAN_VALUES v 
INNER JOIN (
    SELECT 
        dat.Employee_ID,dat.ElementId,dat.SeriesId,dat.Year,dat.Month,dat.Version,dat.Currency,dat.SeriesValidFrom,dat.SeriesValidTo,convert(numeric(18,2),sum(dat.MonSalRatio)) AS ValueLOC,sum(dat.MonFTE)) AS FTE
    FROM (
        SELECT
            pt3.Employee_ID,pt3.SeriesId,pt3.ElementId,pt3.Year,pt3.Month,pt4.MonSalary,pt4.MonFTE,pt3.Version`enter code here`,pt4.Currency,pt3.MonthStart,pt3.MonthEnd,pt4.SeriesValidFrom,pt4.SeriesValidTo,pt4.FteSalFrom,pt4.FteSalTo,(CASE WHEN pt3.MonthStart>pt4.FteSalFrom THEN pt3.MonthStart ELSE pt4.FteSalFrom END) AS ValidFrom,(CASE WHEN pt3.MonthEnd<pt4.FteSalTo THEN pt3.MonthEnd ELSE pt4.FteSalTo END) AS ValidTo,cast((DATEDIFF(day,(CASE WHEN pt3.MonthStart>pt4.SeriesValidFrom THEN pt3.MonthStart ELSE pt4.SeriesValidFrom END),(CASE WHEN pt3.MonthEnd<pt4.SeriesValidTo THEN pt3.MonthEnd ELSE pt4.SeriesValidTo END))+1) AS float)/
            cast((DATEDIFF(day,pt3.MonthEnd)+1) AS float)*pt4.MonSalary AS MonSalRatio,pt3.MonthEnd)+1) AS float)*pt4.MonFTE AS MonFteratio
        FROM (
                SELECT v.*,cast(cast(v.Year AS varchar(4)) + ''-'' +  right(''0'' + cast(v.Month AS varchar(2)),2) + ''-01'' AS date) AS MonthStart,dateadd(day,-1,dateadd(month,1,2) + ''-01'' AS date))) AS MonthEnd
                FROM ##STAFF_PLAN_VALUES AS v
            ) AS pt3 INNER JOIN (
                SELECT
                    pt1.Employee_ID,pt1.SeriesID,pt1.ElementId,pt1.ValidFrom AS SeriesValidFrom,pt1.ValidTo AS SeriesValidTo,pt2.StartDate AS FteSalFrom,pt2.EndDate AS FteSalTo,pt1.Version,pt1.PLCode,pt1.LegalEntityId,pt1.BusinessUnitId,pt1.DepartmentId,pt1.Currency,pt2.MonFTE,pt2.MonSalary
                    FROM
                    ##STAFF_PLAN_ELEMENTS AS pt1
INNER JOIN (SELECT C.Employee_ID,C.StartDate,C.Salary,0 AS FTE,C.Salary/12) AS MonSalary,0 AS MonFTE,''1SAL'' AS ElementId,isnull((SELECT max(dateadd(day,D.StartDate)) FROM ##salseq AS D WHERE C.Employee_ID=D.Employee_ID AND C.SeqID=D.SeqID-1),C.FinalEndDate) AS EndDate FROM ##salseq AS C 
UNION SELECT C.Employee_ID,0 AS Salary,C.FTE,0 AS MonSalary,C.FTE) AS MonFTE,D.StartDate)) FROM ##fteseq AS D WHERE C.Employee_ID=D.Employee_ID AND C.SeqID=D.SeqID-1),C.FinalEndDate) AS EndDate 
FROM ##fteseq AS C UNION SELECT 
C.Employee_ID,''2SOC'' AS ElementId,C.FinalEndDate) AS EndDate 
FROM ##fteseq AS C
UNION SELECT C.Employee_ID,C.Travel,NULL AS FTE,C.Travel/12) AS MonSalary,NULL AS MonFTE,''3TRA'' AS ElementId,D.StartDate)) FROM ##traseq AS D WHERE C.Employee_ID=D.Employee_ID AND C.SeqID=D.SeqID-1),C.FinalEndDate) AS EndDate 
FROM ##traseq AS C
UNION
SELECT 
C.Employee_ID,C.Pension,C.Pension/12) AS MonSalary,''4PPL'' AS ElementId,D.StartDate)) FROM ##pplseq AS D WHERE C.Employee_ID=D.Employee_ID AND C.SeqID=D.SeqID-1),C.FinalEndDate) AS EndDate 
                        FROM ##pplseq AS C
                        UNION
                        SELECT 
                            C.Employee_ID,C.FinalEndDate) AS EndDate 
                        FROM ##fteseq AS C
                        UNION
                        SELECT 
                            C.Employee_ID,C.Other,C.Other/12) AS MonSalary,''5OTH'' AS ElementId,D.StartDate)) FROM ##othseq AS D WHERE C.Employee_ID=D.Employee_ID AND C.SeqID=D.SeqID-1),C.FinalEndDate) AS EndDate 
                        FROM ##othseq AS C
                    ) AS pt2 ON pt1.Employee_ID=pt2.Employee_ID AND pt1.ElementId=pt2.ElementId AND pt1.ValidFrom <= pt2.EndDate AND pt1.ValidTo >= pt2.StartDate
            ) AS pt4 ON pt3.Employee_ID=pt4.Employee_ID AND pt3.ElementId=pt4.ElementId AND pt3.SeriesID=pt4.SeriesID AND pt3.Year = ''' +@year+''' AND pt3.Version = pt4.Version 
            AND pt3.MonthStart <= pt4.FteSalTo AND pt3.MonthEnd >= pt4.FteSalFrom
            AND pt3.MonthStart <= pt4.SeriesValidTo AND pt3.MonthEnd >= pt4.SeriesValidFrom
        ) AS dat
    GROUP BY dat.Employee_ID,dat.SeriesValidTo
) AS sf 
ON v.Employee_ID=sf.Employee_ID AND v.ElementId=sf.ElementId AND v.SeriesId=sf.SeriesId AND v.Year=sf.Year AND v.Version=sf.Version AND v.Month=sf.Month
'

print @sql1

结果:正如我们可以得出的那样,它没有使用完整的字符串

UPDATE v
SET v.ValueLoc = (sf.ValueLoc*isnull(sf.FTE,pt3.Version,cast(cast(v.Year AS varchar(4)) + '-' +  right('0' + cast(v.Month AS varchar(2)),2) + '-01' AS date) AS MonthStart,2) + '-01' AS date))) AS MonthEnd
                FROM ##STAFF_PLAN_VALUES AS v
            ) AS pt3 INNER JOIN (
                SELECT
                    pt1.Employee_ID,'1SAL' AS ElementId,'2SOC' AS ElementId,'3TRA' AS ElementId,'4PPL' AS ElementId,C.FTE) AS Mo

感谢您的帮助。

sql Server版本: Microsoft sql Server 2017(RTM-CU18)(KB4527377)-14.0.3257.3(X64) Windows Server 2016 Datacenter 10.0(内部版本14393:)(管理程序)上的标准版(64位)

解决方法

真正的问题不是数据类型,是T-SQL中的Object命令,它对NVARCHAR的限制为4000个字符,对VARCHAR数据类型的限制为8000个字符。当NVARCHAR(MAX)与NVARCHAR(4000)等效时,这可以回溯到一些旧的SQL Server。他们更改了(MAX)含义,但没有更改print()函数。

最简单的解决方案是避免打印它。如果您尝试插入或执行代码,它将按预期工作。

对于截止字符,快速指南类似于:

  • print()将从第4000个字符处截断该字符串。
  • NVARCHAR (4000)将从第8000个字符处截断该字符串。
  • VARCHAR (8000)NVARCHAR (MAX)足够大,可以容纳任何通用字符串(〜2GB)。

以下是临界值和VARCHAR (MAX)问题的可重现示例:

print()