【问题标题】:SQL Server Dynamic SQL - Get output from list of tablesSQL Server 动态 SQL - 从表列表中获取输出
【发布时间】:2021-05-03 12:54:22
【问题描述】:

我正在尝试遍历包含表名列表的临时表变量。我想简单地计算每个表中 DateTracked 列大于 30 天的行。我无法动态更改 FROM @tblName 变量以存储记录数,然后将其插入到我的跟踪表中。最终我将使用一个游标来遍历每个,但我只想先为单个表获取这个逻辑。这是我的测试代码:

DECLARE @tblName as NVARCHAR(MAX)
DECLARE @q as NVARCHAR(MAX)
SET @q = 'Select Count(DateTracked) FROM Audit.' + @tblName + ' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'

--DECLARE @tblNameTable TABLE
--(
--  tableName NVARCHAR(MAX)
--)
--INSERT INTO @tblNameTable VALUES (N'myTestTable')

DECLARE @ExpectedRecordsToMove AS TABLE (col1 int)
INSERT INTO @ExpectedRecordsToMove EXECUTE sp_executesql @q, N'@tblName nvarchar(500)', @tblName = 'myTestTable'

SELECT * FROM @ExpectedRecordsToMove

【问题讨论】:

  • 您不能参数化@q,因为即使在动态SQL 中也不能参数化表名; 整个语句 必须动态生成,因为@tblName 必须插入到文本中。如果您在生成 @q 之前设置 @tblName,您将获得与循环相同的效果。
  • 不,这正是不会起作用的。同样,表名不能是参数。 'SELECT ...' + @tblName + ' WHERE ...' 是正确的,但是 @tblName 必须已经有一个值,并且不应作为参数传递。在您的示例中,DECLARE @tblName AS SYSNAME = 'myTestTable'.
  • 我现在明白了。生成该语句后,如何获取 sp_executesql 的输出并将其插入我可以引用的变量中?
  • 不确定问题出在哪里——INSERT ... EXEC 适用于表变量,因此其余代码都可以。 INSERT 当然不应该发生在动态生成的语句中,因为那样你就不能引用外部范围的变量。
  • 我现在明白了。我声明了一个局部变量,而不是一个临时表变量。它现在似乎工作了。

标签: sql sql-server dynamic-sql


【解决方案1】:

找到解决方案。

DECLARE @tblName as NVARCHAR(MAX) = 'tblAutoDispatch_DispatchStatus_Map_Tracking'
DECLARE @q as NVARCHAR(MAX) = 'SELECT Count(DateTracked) FROM Audit.' + @tblName + ' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'

DECLARE @ExpectedRecordsToMove TABLE
(
    ExpectedRecordsToMove Int
)
INSERT INTO @ExpectedRecordsToMove
EXECUTE sp_executesql @q

SELECT * FROM @ExpectedRecordsToMove

注意:OP 就问题提供的答案。

【讨论】:

    【解决方案2】:

    sp_executesql 也允许输出参数。可以在内部查询中分配变量并返回值。

    DECLARE @tblName AS NVARCHAR(MAX) = N'tblAutoDispatch_DispatchStatus_Map_Tracking';
    DECLARE @q AS NVARCHAR(MAX)
        = N'SELECT @rowcount = COUNT(DateTracked) FROM Audit.' + @tblName
          + N' WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))';
    
    DECLARE @rowcount INT;
    
    EXECUTE sp_executesql
        @query = @q,
        @parameters = N'@rowcount INT OUTPUT',
        @rowcount = @rowcount OUTPUT;
    
    SELECT
        @rowcount;
    

    【讨论】:

      【解决方案3】:

      您实际上可以在没有光标的情况下执行此操作,只需一次构建查询

      • 您必须使用QUOTENAME 以确保表名不会导致语法错误
      • 您不能参数化表名,它必须直接插入到动态 SQL 中
      DECLARE @tablenames TABLE (schemaName sysname, tblName sysname);
      --  insert schema and table names
      
      DECLARE @sql nvarchar(max) = N'
      SELECT
          ISNULL(tblName, ''Grand Total'') AS tblName,
          SUM(rowcount) AS rowcount
      FROM (
      ' +
      (
        SELECT STRING_AGG(CAST(
      N'    SELECT ' + QUOTENAME(tn.schemaName, '''') + N'.' + QUOTENAME(tn.tblName, '''') + N' AS tblName,
                 Count(DateTracked) AS rowCount
          FROM ' + QUOTENAME(tn.schemaName) + N'.' + QUOTENAME(tn.tblName) + N'
          WHERE DateTracked > DATEADD(dd, -30, CAST(GETDATE() as date))'
        AS nvarchar(max)), N'
      UNION ALL
      ')
        FROM @tablenames tn
        JOIN sys.tables t ON tn.schemaName = SCHEMA_NAME(t.schema_id) AND tn.tblName = t.name
      ) + N'
      ) AS tables
      GROUP BY ROLLUP(tblName);
      ';
      
      PRINT @sql;  -- for testing
      EXEC (@sql);
      
      • 如果您的 SQL Server 版本上没有STRING_AGG,则必须改用FOR XML

      【讨论】:

      • 我没有在原始帖子中描述这一点,但我使用游标循环 200 个表和 WHILE LOOP 批量删除/输出,所以我可以使用游标这种情况,因为没有性能影响,而且我仍在为我的实际事务使用基于 SET 的操作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-31
      • 2020-06-28
      • 2010-09-13
      • 1970-01-01
      • 2012-01-21
      • 2017-11-19
      • 2013-09-27
      相关资源
      最近更新 更多