对表X的所有字段进行动态计数(字段)的SQL查询

对表X的所有字段进行动态计数(字段)的SQL查询,sql,sql-server,Sql,Sql Server,这本来是件容易的事,但却把我难住了 您可以使用一行程序轻松手动返回表中每个字段的计数,例如: select count(FIELD1) from TABLE1 --42,706 select count(FIELD5) from TABLE1 --42,686 select count(FIELD9) from TABLE1 --2,918 如果您想以同样的方式查看几十个表,并且要求您提前知道字段的名称,那么这将是一个缓慢而痛苦的过程 如果有一个脚本可以连接到任何数据库,只需给它提

这本来是件容易的事,但却把我难住了

您可以使用一行程序轻松手动返回表中每个字段的计数,例如:

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'
即使我的赤裸裸的方法(明确地击中一个领域而不是全部领域)也有缺陷:

结果是42706。回想一下我上面的例子,FIELD9只包含2918个值

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

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
但这仍然让我们回到了最初的问题,即返回42706而不是2918

我正在运行SQL Server 2008 R2,如果有什么不同的话。

您的查询:

SELECT @FIELDNAME, COUNT(@FIELDNAME) FROM TABLE1
不计算
FIELD9
@FIELDNAME
被视为常量。这就像做一个
计数(*)

您应该使用动态sql:

DECLARE @sql VARCHAR(MAX)

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

EXEC(@sql)

要获取所有列并在单个结果集中返回它,而不使用
临时表
光标

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)

只需设置@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

谢谢你的解释!动态SQL在一次查询一个字段时起作用。但是,如果我回去尝试获取表中所有字段的输出,例如使用光标,我会得到每一行作为单独的结果。理想情况下,输出将是一个单一的结果集,然后我可以构建该结果集以在其他列中返回其他重要的细节。哇。你赢了。对不起,埃里克。这在速度上与光标相当(即使是在包含2051408条记录的表上),但这对眼睛来说要容易得多。“可读性很重要。”在点击信息SCHEMA.COLUMNS时,只需要“和数据类型不在('text'、'ntext'、'image')”,或者其他一些巧妙避免这些数据类型错误的方法。@user2585435通过多行赋值进行字符串连接没有记录,可能会失败。看@Eric这让我有点不知所措,但我认为这篇文章是说在这种情况下你应该警惕ORDER BY@user2585435你是对的,这对这个案子来说应该是安全的!只要在“INSERT@TableAndColumn”WHERE子句中添加“and system_type_id not in(34,35,99)”,我就接受了!('text、ntext和image数据类型不能进行比较或排序,除非使用IS NULL或LIKE运算符。')
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