SQL查询为表X的所有字段动态COUNT(FIELD)

时间:2015-03-12 02:34:15

标签: sql sql-server

这应该是一件容易的事情,但它让我完全难过。

您可以使用oneliner手动轻松返回表格中每个字段的计数,如:

select count(FIELD1) from TABLE1   --42,706
select count(FIELD5) from TABLE1   --42,686
select count(FIELD9) from TABLE1   --2,918

如果您想以相同的方式查看几十个表格,这会很慢且很痛苦,并且需要您事先知道字段的名称。

有一个脚本可以连接到任何数据库,只需输入一个表名,它会自动返回该表的每个字段的计数吗?

似乎你可以完成一半的工作:

select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'TABLE1'

即使我的准系统接近(明确地击中一个字段而不是全部字段),有些东西也存在缺陷:

declare @TABLENAME varchar(30), @FIELDNAME varchar(30)
set @TABLENAME = 'TABLE1'

set @FIELDNAME = (select top 1 COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = @TABLENAME
and COLUMN_NAME = 'FIELD9')

select @FIELDNAME, count(@FIELDNAME) from TABLE1

结果是42,706。回想一下上面的例子,FIELD9只包含2,918个值。

即使这不是问题,更动态的查询会将最后一行替换为:

select @FIELDNAME, count(@FIELDNAME) from @TABLENAME

但SQL Server返回:

Must declare the table variable "@TABLENAME".

所以我可以通过使用临时表重构查询来避免这种情况:

declare @FIELDNAME varchar(30)

set @FIELDNAME = (select top 1 COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'TABLE1'
and COLUMN_NAME = 'FIELD9')

if OBJECT_ID('TEMPDB..#TEMP1') is not null
    drop table #TEMP1

select *
into #TEMP1
from TABLE1  --still not exactly dynamic!

select @FIELDNAME, count(@FIELDNAME) from #TEMP1

但这仍然让我们回到原来的问题,即返回42,706而不是2,918。

我正在运行SQL Server 2008 R2,如果它有任何区别。

2 个答案:

答案 0 :(得分:2)

您的查询:

SELECT @FIELDNAME, COUNT(@FIELDNAME) FROM TABLE1

不计算FIELD9@FIELDNAME被视为常量。这就像做COUNT(*)

你应该使用动态sql:

DECLARE @sql VARCHAR(MAX)

SET @sql = 'SELECT ''' + @fieldName + ''', COUNT([' + @fieldName + ']) FROM [' + @tableName + ']'

EXEC(@sql)

要获取所有列并将其返回到单个结果集中,而不使用Temporary TableCURSOR

DECLARE @sql NVARCHAR(MAX) = ''

SELECT @sql = @sql +
'SELECT ''' + COLUMN_NAME + ''' AS ColName, COUNT([' + COLUMN_NAME + ']) FROM [' + @tableName + ']' + CHAR(10) +
'UNION ALL' + CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @tableName

SELECT @sql = LEFT(@sql, LEN(@sql) - 10)
EXEC(@sql)

答案 1 :(得分:0)

设置@TargetTableName即可完成工作

DECLARE @TargetTableName sysname = '*'

SET NOCOUNT ON
DECLARE @TableName sysname, @ColumnName sysname, @Sql nvarchar(max)
DECLARE @TableAndColumn table
(
    TableName sysname,
    ColumnName sysname
)

DECLARE @Result table
(
    TableName sysname,
    ColumnName sysname,
    NonNullRecords int
)

INSERT @TableAndColumn
SELECT o.name, c.name FROM sys.objects o INNER JOIN sys.columns c ON o.object_id = c.object_id 
WHERE (o.name = @TargetTableName OR @TargetTableName = '*') AND o.type = 'U' AND c.system_type_id NOT IN (34, 35, 99) -- 34:image 35:text 99:ntext
ORDER BY c.column_id


DECLARE column_cursor CURSOR FOR SELECT TableName, ColumnName FROM @TableAndColumn
OPEN column_cursor

FETCH NEXT FROM column_cursor 
INTO @TableName, @ColumnName

WHILE @@FETCH_STATUS = 0
BEGIN

    SELECT @Sql = 'SELECT ''' + @TableName + ''' AS TableName, ''' + @ColumnName + ''' AS ColumnName, COUNT([' + @ColumnName + ']) AS NonNullRecords FROM [' + @TableName + ']'
    print @Sql

    INSERT @Result
    EXEC (@Sql) 

    FETCH NEXT FROM column_cursor 
    INTO @TableName, @ColumnName
END 
CLOSE column_cursor;
DEALLOCATE column_cursor;

SET NOCOUNT OFF

SELECT * FROM @Result