动态SQL查询以查找表的所有列中缺失值的计数

3
我可以帮助你翻译这段有关IT技术的内容。该内容需要动态SQL查询来查找表中每个列的缺失值数量。由于该表包含40个列,为每个列编写缺失计数很冗长,因此我们能否动态地实现呢?我尝试编写以下动态查询,但出现了错误:

必须声明标量变量“@sql”

查询:

Declare @sql nvarchar(max)  
Declare @columnlist nvarchar(max)   
Declare @FieldName nvarchar(max)    

set @columnlist = 'Column 1,Column2 ,Column3 ,Column4 ,Column5 ,Column6 ,Column7 ,Column8 ,Column9 ,Column10 ,Column11 ,Column12,Column13'  

set @FieldName = 'Column 1,Column2 ,Column3 ,Column4 ,Column5 ,Column6 ,Column7 ,Column8 ,Column9 ,Column10 ,Column11 ,Column12,Column13'

set @sql = 'SELECT COUNT(*)-COUNT(' + @columnlist + ') as '+ @FieldName +'from table_name' 

exec (@sql)

1
您打错了。第二个查询 set @FieldName 中没有起始单引号。 - Nisarg Shah
尝试使用双引号 ""select 查询中。 - Bhargav Chudasama
@NisargShah 我已经更新了,但仍然出现相同的错误。 - Promila
你能分享具体的错误信息吗? - Bhargav Chudasama
1
我认为将这40个表达式复制粘贴下来会比写两次字段列表,然后构建代码将列列表字符串转换为某种表达式更快,以便能够动态构建表达式列表。 - Wolfgang Kais
显示剩余6条评论
2个回答

2

试试这个:

DECLARE @sql nvarchar(max) = N'SELECT';
DECLARE @table_name nvarchar(256) = N'YourTableName'

SELECT @sql = @sql + ' COUNT(*)-COUNT(' + QUOTENAME(COLUMN_NAME) + ') as '+ QUOTENAME(COLUMN_NAME) + N','
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = N'dbo'
  AND TABLE_NAME = @table_name

SET @sql = LEFT(@sql, LEN(@sql) - 1) + ' from ' + @table_name

EXEC (@sql)

新增

好的,下面是一个示例,说明如何使用类似的方法来计算每一列中至少有一个重复值的数量:

DECLARE @sql nvarchar(max) = N'WITH duplicates AS (SELECT';
DECLARE @table_name nvarchar(256) = N'YourTableName';

SELECT @sql = @sql 
  + N' CASE WHEN COUNT(' + QUOTENAME(COLUMN_NAME) 
  + N') OVER (PARTITION BY ' + QUOTENAME(COLUMN_NAME) 
  + N') > 1 THEN ' + QUOTENAME(COLUMN_NAME)  
  + N' END as '+ QUOTENAME(COLUMN_NAME) + N','
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = N'dbo'
  AND TABLE_NAME = @table_name;

SET @sql = LEFT(@sql, LEN(@sql) - 1) + ' from ' + @table_name + ') SELECT';

SELECT @sql = @sql 
  + N' COUNT(DISTINCT ' + QUOTENAME(COLUMN_NAME) 
  + N') as '+ QUOTENAME(COLUMN_NAME) + N','
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = N'dbo'
  AND TABLE_NAME = @table_name;

SET @sql = LEFT(@sql, LEN(@sql) - 1) + ' from duplicates';

EXEC (@sql);

您可以看到它使用了2个SELECT语句,第一个创建了一个CTE,用NULL替换了所有没有重复值的值:

CASE WHEN COUNT([C1]) OVER (PARTITION BY [C1]) > 1 THEN [C1] END as [C1]

第二种方法使用COUNT DISTINCT来计算剩下的内容。这样做,NULL值将不会被计算在内。

很好地使用了QUOTENAME,值得赞美!但是,在最后的' from ' + @table_name处不小心忘记了它(如果@table_name是输入参数,则可能是最重要的地方)。 ;) - Thom A
@Larnu 谢谢!说实话,我没有忘记它,我是故意省略了它,因为表名可能是唯一“已知”的事情,并且至少在脚本中是手动输入的。但你是对的,如果作为参数传递,这完全有意义。 :-) - Wolfgang Kais
@WolfgangKais,如果我需要使用动态查询查找每个列的重复数据计数,该如何做?这是否可能?我尝试使用上面提供的示例,但它没有起作用。我正在为每个列使用Group By,这不是正确的解决方法。下面是执行Print命令时显示的语法示例...例如:SELECT [C1],COUNT([C1])作为[id] FROM table_name GROUP BY [C1] HAVING COUNT([C1])> 1,[C2],COUNT([C2])作为[c2] FROM table_name GROUP BY [c2] HAVING COUNT([c2])> 1....请建议一些解决方案。 - Promila
@Promila 上述查询创建了一行数据。如果“重复计数”也应该生成一行数据,那么对于列C1的“重复次数”为20,这对您意味着什么?它可能意味着一个值重复出现了20次,或者10个值每个有一个重复(或介于两者之间)。或者您想看到1或10(至少有一个重复的值的数量)?我建议首先明确一些关于一列计算的“规范”,然后我们可以找出是否有意义在一个查询中对多个列执行相同的操作。 - Wolfgang Kais
@WolfgangKais 我需要每个列值的重复数据计数。我不确定是否可以在单个查询中实现。 - Promila
@Promila,我刚刚为你描述的第一个例子添加了一个示例(GROUP BY [C1] HAVING COUNT([C1] )>1)。如果现在你想要计算每个值的重复次数,请尝试想象一下这个查询结果应该如何显示(只是它应该看起来的样子,而不是它是如何完成的)。如果你能做到,请告诉我,因为我做不到。 - Wolfgang Kais

0

试试这个

IF OBJECT_ID('TempDb..#MissingList') IS NOT NULL
    DROP TABLE #MissingList

CREATE TABLE #MissingList
(
    SeqNo INT IDENTITY(1,1),
    TABLE_NAME VARCHAR(255),
    COLUMN_NAME VARCHAR(255),
    MissingCount INT DEFAULT(0)
)

DECLARE @SQL NVARCHAR(MAX)

SELECT
    @SQL = REPLACE(REPLACE(L.List,'&#x0D',''),';','')
    FROM
    (
        SELECT 
            '
            INSERT INTO #MissingList(TABLE_NAME,COLUMN_NAME,MissingCount)
            SELECT TABLE_NAME = ''['+LTRIM(RTRIM(TABLE_SCHEMA))+'].['+LTRIM(RTRIM(TABLE_NAME))+']'',COLUMN_NAME = '''+LTRIM(RTRIM(COLUMN_NAME))+''',MissingCount = COUNT(1) - COUNT(['+LTRIM(RTRIM(COLUMN_NAME))+'])
            FROM ['+LTRIM(RTRIM(TABLE_SCHEMA))+'].['+LTRIM(RTRIM(TABLE_NAME))+']' AS [text()]
            FROM INFORMATION_SCHEMA.COLUMNS
            FOR XML PATH('')
    )L(LIST)

EXEC(@SQL)

SELECT
    *
    FROM #MissingList

1
使用 '[ + @Variable + ']' 无法阻止注入,也无法处理命名不当的对象(确实存在带有右括号 ] 的对象,相信我)。QUOTENAME 是一个更好的选择。 - Thom A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接