了解动态 SQL

Understanding dynamic SQL

在尝试理解动态 SQL 的过程中,我尝试通过动态插入 sys.tables 的每个表中的行数计数来开始简单操作。

这是我的代码:

SELECT 
    Name, ROW_NUMBER() OVER (ORDER BY NEWID() ) AS SomeNumb
INTO
    #Dyn
FROM 
    sys.tables

CREATE TABLE ##Results (Cnt INT)    

DECLARE @Table NVARCHAR(100)
DECLARE @Counter INT

SET @Counter = 1
SET @Table = (SELECT Name FROM #Dyn WHERE SomeNumb = @Counter)

DECLARE @Sql NVARCHAR(1000)

WHILE @Counter <= (SELECT COUNT(*) FROM #Dyn)
BEGIN
    INSERT INTO ##ResultsTable
    SELECT @Sql = 'SELECT COUNT(*) AS Cnt FROM #Dyn WHERE 
    Name = ' + @Table + 'AND SomeNumb = ' + @Counter
    EXECUTE (@Sql)

    SET @Counter = @Counter + 1
    SET @Sql = ''
END

SELECT * FROM ##ResultsTable

唯一的好处就是不会出错。尽管这可能会给我一些方向。我知道我的 ResultsTable 存在范围问题,但我认为使用 ## 而不是 # 可以解决这个问题。

任何指点将不胜感激。

这里有一个经过审核的脚本。

IF OBJECT_ID('tempdb..#Dyn') IS NOT NULL
    DROP TABLE #Dyn

SELECT Name, ROW_NUMBER() OVER (ORDER BY NEWID() ) AS SomeNumb
INTO #Dyn
FROM sys.tables

IF OBJECT_ID('tempdb..#ResultsTable') IS NOT NULL
    DROP TABLE #ResultsTable

CREATE TABLE #ResultsTable (TotalRowCount INT)

DECLARE @Counter INT = 1
DECLARE @Sql NVARCHAR(MAX)

WHILE @Counter <= (SELECT COUNT(*) FROM #Dyn)
BEGIN

    DECLARE @Table NVARCHAR(100) = (SELECT Name FROM #Dyn WHERE SomeNumb = @Counter)

    SELECT @Sql = '
        INSERT INTO #ResultsTable (TotalRowCount)
        SELECT COUNT(*) AS Cnt 
        FROM #Dyn 
        WHERE Name = ''' + @Table + ''' AND SomeNumb = ' + CONVERT(NVARCHAR(10), @Counter)

    EXECUTE (@Sql)

    SET @Counter += 1

END


SELECT * FROM #ResultsTable

有些事情要提一下:

  • 您可以在 EXEC 外部创建临时 table 并将其插入动态 SQL。
  • 您的 COUNT()#Dyn table 将始终 return 1 因为只有 1 条记录具有特定的 table 名称。
  • 在动态 SQL:
  • 上打印 varchar 值(如 @Table)时,您需要添加额外的引号
  • 构建动态 SQL(如 @Counter)时,您需要将所有非文字值转换为 VARCHARNVARCHAR

编辑:如果你想要每个 table 的实际计数,那么你不需要查询 #Dyn!

SELECT @Sql = '
    INSERT INTO #ResultsTable (TotalRowCount)
    SELECT COUNT(*) AS Cnt 
    FROM ' + QUOTENAME(@Table) +

EXECUTE (@Sql)

与其使用 CURSORWHILE 循环,一种方法是使用 sys.tablessys.schemas 以及 FOR XML PATH:

DECLARE @SQL nvarchar(MAX);
SET @SQL = (SELECT N'SELECT ''' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.name) +''' AS ObjectName, COUNT(*) AS [RowCount]' + NCHAR(10) +
                   N'FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + N';' + NCHAR(10)
            FROM sys.tables t
                 JOIN sys.schemas s ON t.SCHEMA_ID = s.SCHEMA_ID
            FOR XML PATH(N''))

PRINT @SQL;
CREATE TABLE #Cnt (ObjectName sysname, [RowCount] int);
INSERT INTO #Cnt (ObjectName, [RowCount])
EXEC sp_executesql @SQL;

SELECT *
FROM #cnt;

DROP TABLE #cnt;

或者,您可以使用未记录的过程 sp_msforeachtable:

CREATE TABLE #Cnt (ObjectName sysname, [RowCount] int);
INSERT INTO #Cnt (ObjectName, [RowCount])
EXEC sp_msforeachtable N'SELECT ''?'' AS ObjectName, COUNT(*) AS [RowCount]
FROM ?;';

SELECT *
FROM #cnt;

DROP TABLE #cnt;

但是,如果我没记错的话,后一种方法实际上使用了 CURSOR