【问题标题】:SqlSever: Multiple CTE in EXECUTE SP_EXECUTESQLSql Server:EXECUTE SP_EXECUTESQL 中的多个 CTE
【发布时间】:2019-07-26 09:21:56
【问题描述】:
    DECLARE @sqlString nvarchar(500); SET @sqlString = ';WITH Data_CTE 
        AS
        (
            SELECT pj.ProjectID, pj.ProjectName, pj.ProjectOwner, cs.CustomerName
            FROM Projects as pj
            LEFT OUTER JOIN [Customers] cs
                ON pj.CustomerId = cs.CustomerID
            WHERE  pj.ProjectOwner =  @ename 
                AND (pj.ProjectStatus = 1 OR pj.ProjectStatus = 0)
        )
        SELECT *, (SELECT COUNT(*) FROM Data_CTE)  AS TotalRows
        FROM Data_CTE
        ORDER BY ProjectName
        OFFSET 0 * 10 ROWS
        FETCH NEXT 10 ROWS ONLY;'


SET @ParmDefinition = N'@ename varchar(100)';
SET @ename = 'mohapam@test.com';

EXECUTE SP_EXECUTESQL @sqlString, @ParmDefinition, @ename = @ename;

在这里,我试图根据分页获取记录数,并获得计数总数。当我执行上述查询时,它在语法 'COU 处显示错误。

请建议任何其他方式来获取总计数的记录。

【问题讨论】:

  • 你的@sqlString声明在哪里?它应该是NVARCHAR(MAX),但错误消息强烈表明它正在被切断。
  • 你为什么要使用sp_executesql?您已标记动态 SQL,但该语句不是动态的。
  • @JeroenMostert, sqlString 是 NVARCHAR(500);
  • @Larnu,我正在使用 sp_executesql 来提高性能。
  • 水晶球做得很好@JeroenMostert :) Manas,如果您使用PRINT @sqlString;,请注意该值已被截断。 sp_executesql 的第一个参数是 nvarchar(MAX)。但是,正如所讨论的,它甚至不需要sp_executesql

标签: sql-server common-table-expression sp-executesql


【解决方案1】:

如前所述,这里根本不需要sp_executesql。动态 SQL 是指您拥有一个动态的对象(顾名思义)。举个简单的例子,你不能有类似的东西:

DECLARE @TableName sysname = N'MyTable',
        @Name varchar(50) = 'Jane Smith';

SELECT *
FROM @TableName
WHERE [Name] = @Name;

这将返回一个错误,指出表变量@TableName 尚未声明。相反,您将执行以下操作:

DECLARE @TableName sysname = N'MyTable',
        @Name varchar(50) = 'Jane Smith';

DECLARE @SQL nvarchar(MAX);

SET @SQL = N'SELECT * FROM ' + QUOTENAME(@TableName) + N'WHERE [Name] = @Name;';

EXEC sp_executesql @SQL, N'@name varchar(50)', @Name;

但是,在您的陈述中,您没有动态对象;我们可以很容易地看到这一点,因为您没有在查询中注入任何对象名称。

此外,我们可以将子查询(SELECT COUNT(*) FROM Data_CTE) 更改为窗口函数,从而在表格上节省额外的罐头。这给出了下面的最终查询:

DECLARE @email varchar(100) = 'mohapam@test.com';

WITH Data_CTE AS
    (SELECT pj.ProjectID,
            pj.ProjectName,
            pj.ProjectOwner,
            cs.CustomerName,
            COUNT(*) OVER () AS TotalRows
     FROM Projects pj
          LEFT OUTER JOIN [Customers] cs ON pj.CustomerId = cs.CustomerID
     WHERE pj.ProjectOwner = @ename
       AND (pj.ProjectStatus = 1
         OR pj.ProjectStatus = 0))
SELECT ProjectID,
       ProjectName,
       ProjectOwner,
       CustomerName,
       DC.TotalRows
FROM Data_CTE DC
ORDER BY ProjectName OFFSET 0 * 10 ROWS FETCH NEXT 10 ROWS ONLY;

至于 为什么 您遇到了错误,如果您查看问题中文字字符串的长度,它超过 500 个字符,但您声明您将 @sqlString 定义为varchar(500)。结果,该值被截断,因此 SQL 不起作用。

【讨论】:

    【解决方案2】:

    您可以使用OVER 子句获取总行数。您无需使用CTE 使其复杂化。使用您的查询,如下所示。

    SELECT pj.ProjectID, pj.ProjectName, pj.ProjectOwner, cs.CustomerName, COUNT(*) OVER() AS TotalRows
    FROM Projects as pj
    LEFT OUTER JOIN [Customers] cs
        ON pj.CustomerId = cs.CustomerID
    WHERE  pj.ProjectOwner =  @ename 
        AND (pj.ProjectStatus = 1 OR pj.ProjectStatus = 0
    ORDER BY pj.ProjectName
    OFFSET 0 * 10 ROWS
    FETCH NEXT 10 ROWS ONLY;
    

    Click here for more details about OVER Clause

    【讨论】:

    • 是的,这行得通。但是,与 CTE 相比,它的性能成本更高。所以我用 CTE 而不是 OVER()
    • 我很惊讶您的方法(执行 CTE 中定义的行计数)成本更低。您使用的方法需要对 CustomersProjects 对象进行 2 次扫描;这个方法不会
    • @Larnu,我已经测试并发现COUNT(*) 将给出从WHERE 条件返回的总行数。 OFFSET 不影响 COUNT(*)
    猜你喜欢
    • 2012-10-19
    • 2019-10-05
    • 1970-01-01
    • 2019-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-13
    • 1970-01-01
    相关资源
    最近更新 更多