单个 SQL 查询以在数据库的所有列中查找空值
Single SQL query to find null values in all columns in a data base
我想确定所有 tables.I 中每列中空值的数量 有一个数据库,它由大约 250 个组成 tables.Most 其中 use.The 问题是几乎所有 table 都包含不需要的列,这些列是为短期 use.Now 创建的,我们想为所有 tables.Since 识别具有空值的列 table 的计数很大,时间很长less.I 想知道一种最简单的方法来识别列中每个 table 的空记录数。
我尝试了从 internet.But 获得的这个查询,我必须手动给每个 table 名称。
DECLARE @cols1 NVARCHAR(MAX);
DECLARE @sql NVARCHAR(MAX);
SELECT @cols1 = STUFF((
SELECT ', COUNT(CASE WHEN ISNULL(CONVERT(NVARCHAR(MAX), [' + t1.NAME + ']),'''' ) = '''' THEN 1 END) AS ' + t1.name
FROM sys.columns AS t1
WHERE t1.object_id = OBJECT_ID('Area')
-- ORDER BY ', COUNT([' + t1.name + ']) AS ' + t1.name
FOR XML PATH('')
), 1, 2, '');
SET @sql = '
SELECT ' + @cols1 + '
FROM Area
'
EXEC(@sql)
请帮助我改进查询获取结果。
谢谢
这是一团糟,但它有效:
DECLARE @SQL nvarchar(MAX),
@CRLF nchar(2) = NCHAR(13) + NCHAR(10);
CREATE TABLE #NullCounts (SchemaName sysname,
TableName sysname,
ColumnName sysname,
NULLCount bigint);
DECLARE @Delimiter nchar(3) = ',' +@CRLF;
SET @SQL = STUFF((SELECT @CRLF + @CRLF +
N'WITH Counts AS(' + @CRLF +
N' SELECT N' + QUOTENAME(s.[name],'''') +N' AS SchemaName,' + @CRLF +
N' N' + QUOTENAME(t.[name],'''') +N' AS TableName,' + @CRLF +
STRING_AGG(N' COUNT_BIG(CASE WHEN ' + QUOTENAME(c.[name]) + N' IS NULL THEN 1 END) AS ' + QUOTENAME(c.[name]),@Delimiter) WITHIN GROUP(ORDER BY c.column_id) + @CRLF +
N' FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + N' T)' + @CRLF +
N'INSERT INTO #NullCounts(SchemaName, TableName, ColumnName, NULLCount)' + @CRLF +
N'SELECT SchemaName,' + @CRLF +
N' TableName,' + @CRLF +
N' V.ColumnName,' + @CRLF +
N' V.NULLCount' + @CRLF +
N'FROM Counts C' + @CRLF +
N' CROSS APPLY (VALUES' +
STUFF(STRING_AGG(N' (N' + QUOTENAME(c.[name], '''') + N', C.' + QUOTENAME(c.[name]) + N')',@Delimiter) WITHIN GROUP (ORDER BY c.column_id),1,24,N'') + N')V(ColumnName,NULLCount);'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
GROUP BY s.[name], t.[name]
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,4,N'');
--PRINT @SQL; --This is gunna be way longer than 4,000 characters, so you'll want SELECT
EXEC sys.sp_executesql @SQL;
GO
SELECT *
FROM #NullCounts
ORDER BY SchemaName,
TableName,
ColumnName;
GO
DROP TABLE #NullCounts;
是的,我混合了 STRING_AGG
和 FOR XML PATH
,是的,这很碍眼,但是打印的(选择的)SQL 产生了一些非常好的陈述。见下文:
WITH Counts AS(
SELECT N'dbo' AS SchemaName,
N'PerformanceTest' AS TableName,
COUNT_BIG(CASE WHEN TestID IS NULL THEN 1 END) AS [TestID],
COUNT_BIG(CASE WHEN TestTarget IS NULL THEN 1 END) AS [TestTarget],
COUNT_BIG(CASE WHEN TestName IS NULL THEN 1 END) AS [TestName],
COUNT_BIG(CASE WHEN TimeStart IS NULL THEN 1 END) AS [TimeStart],
COUNT_BIG(CASE WHEN TimeEnd IS NULL THEN 1 END) AS [TimeEnd],
COUNT_BIG(CASE WHEN TimeTaken_ms IS NULL THEN 1 END) AS [TimeTaken_ms],
COUNT_BIG(CASE WHEN TotalRows IS NULL THEN 1 END) AS [TotalRows],
COUNT_BIG(CASE WHEN RowSets IS NULL THEN 1 END) AS [RowSets],
COUNT_BIG(CASE WHEN AvgRowsPerSet IS NULL THEN 1 END) AS [AvgRowsPerSet]
FROM [dbo].[PerformanceTest] T)
INSERT INTO #NullCounts(SchemaName, TableName, ColumnName, NULLCount)
SELECT SchemaName,
TableName,
V.ColumnName,
V.NULLCount
FROM Counts C
CROSS APPLY (VALUES(N'TestID', C.[TestID]),
(N'TestTarget', C.[TestTarget]),
(N'TestName', C.[TestName]),
(N'TimeStart', C.[TimeStart]),
(N'TimeEnd', C.[TimeEnd]),
(N'TimeTaken_ms', C.[TimeTaken_ms]),
(N'TotalRows', C.[TotalRows]),
(N'RowSets', C.[RowSets]),
(N'AvgRowsPerSet', C.[AvgRowsPerSet]))V(ColumnName,NULLCount);
WITH Counts AS(
SELECT N'dbo' AS SchemaName,
N'someTable' AS TableName,
COUNT_BIG(CASE WHEN id IS NULL THEN 1 END) AS [id],
COUNT_BIG(CASE WHEN SomeCol IS NULL THEN 1 END) AS [SomeCol]
FROM [dbo].[someTable] T)
INSERT INTO #NullCounts(SchemaName, TableName, ColumnName, NULLCount)
SELECT SchemaName,
TableName,
V.ColumnName,
V.NULLCount
FROM Counts C
CROSS APPLY (VALUES(N'id', C.[id]),
(N'SomeCol', C.[SomeCol]))V(ColumnName,NULLCount);
是的,我真的花了最后 45 分钟写了所有这些...
老实说,这不是入门级的,如果你不了解它,你不应该使用它;而且,我非常怀疑您会找到一个不同的入门级解决方案,而且性能与此一样。 A CURSOR
,例如,虽然可能更容易理解,但这样做真的很慢。
警告: 如果您的数据库中有任何已弃用的数据类型(即 text
),这将失败。如果是这种情况,您将需要确保从 WHERE
中的查询中删除它们。但是,我建议您修复数据类型(text
,例如,已弃用 15 年)。
对于表格结果:
declare @sql nvarchar(max) =
(
select 'union all select (select object_schema_name('+ cast(tableobjectid as varchar(20))+') +''.''+ object_name('+ cast(tableobjectid as varchar(20))+') as "table/@name", count(*) as "table/@rowcount", ' + cols_concat + ' from ' + tablename + ' for xml path(''''), type)' as 'text()'
from
(
select
t.object_id as tableobjectid,
quotename(schema_name(t.schema_id)) + '.' + quotename(t.name) as tablename,
stuff( (select ', col_name(' + cast(c.object_id as varchar(20)) + ',' + cast(c.column_id as varchar(20)) + ') as "table/col/@name", count(*)-count('+case when type_name(c.system_type_id) in ('text', 'ntext', 'image') then ' case when ' + quotename(c.name) + ' is not null then 1 end' else quotename(c.name) end + ') as "table/col", null as "table"'
from sys.columns as c where c.object_id = t.object_id for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '') as cols_concat
from sys.tables as t
where t.is_ms_shipped = 0
) as tbl
for xml path(''), type).value('.', 'nvarchar(max)')
;
select @sql = 'select @xml = (select * from (select cast(null as xml) as "*" '+ @sql + ') as u for xml path('''') )';
declare @x xml;
set transaction isolation level read uncommitted;
exec sp_executesql @sql, N'@xml xml output', @xml = @x output;
set transaction isolation level read committed;
--shred
select
t.col.value('@name[1]', 'varchar(200)') as tablename,
t.col.value('@rowcount[1]', 'int') as tablerowcount,
r.col.value('@name[1]', 'varchar(200)') as columnname,
r.col.value('.[1]', 'int') as null_values
from @x.nodes('table') as t(col)
cross apply t.col.nodes('col') as r(col);
对于多个结果集(每个 table 一个):
declare @sql nvarchar(max) =
(
select 'select object_schema_name('+ cast(tableobjectid as varchar(20))+') +''.''+ object_name('+ cast(tableobjectid as varchar(20))+') as tablename, ' + cols_concat + ' from ' + tablename + ';' as 'text()'
from
(
select
t.object_id as tableobjectid,
quotename(schema_name(t.schema_id)) + '.' + quotename(t.name) as tablename,
stuff( (select ', count(*)-count('+case when type_name(c.system_type_id) in ('text', 'ntext', 'image') then ' case when ' + quotename(c.name) + ' is not null then 1 end' else quotename(c.name) end + ') as "NULL_' + replace(quotename(name), '"', '""')+'"' from sys.columns as c where c.object_id = t.object_id for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '') as cols_concat
from sys.tables as t
where t.is_ms_shipped = 0
) as tbl
for xml path(''), type).value('.', 'nvarchar(max)');
set transaction isolation level read uncommitted;
exec(@sql);
set transaction isolation level read committed;
我想确定所有 tables.I 中每列中空值的数量 有一个数据库,它由大约 250 个组成 tables.Most 其中 use.The 问题是几乎所有 table 都包含不需要的列,这些列是为短期 use.Now 创建的,我们想为所有 tables.Since 识别具有空值的列 table 的计数很大,时间很长less.I 想知道一种最简单的方法来识别列中每个 table 的空记录数。
我尝试了从 internet.But 获得的这个查询,我必须手动给每个 table 名称。
DECLARE @cols1 NVARCHAR(MAX);
DECLARE @sql NVARCHAR(MAX);
SELECT @cols1 = STUFF((
SELECT ', COUNT(CASE WHEN ISNULL(CONVERT(NVARCHAR(MAX), [' + t1.NAME + ']),'''' ) = '''' THEN 1 END) AS ' + t1.name
FROM sys.columns AS t1
WHERE t1.object_id = OBJECT_ID('Area')
-- ORDER BY ', COUNT([' + t1.name + ']) AS ' + t1.name
FOR XML PATH('')
), 1, 2, '');
SET @sql = '
SELECT ' + @cols1 + '
FROM Area
'
EXEC(@sql)
请帮助我改进查询获取结果。
谢谢
这是一团糟,但它有效:
DECLARE @SQL nvarchar(MAX),
@CRLF nchar(2) = NCHAR(13) + NCHAR(10);
CREATE TABLE #NullCounts (SchemaName sysname,
TableName sysname,
ColumnName sysname,
NULLCount bigint);
DECLARE @Delimiter nchar(3) = ',' +@CRLF;
SET @SQL = STUFF((SELECT @CRLF + @CRLF +
N'WITH Counts AS(' + @CRLF +
N' SELECT N' + QUOTENAME(s.[name],'''') +N' AS SchemaName,' + @CRLF +
N' N' + QUOTENAME(t.[name],'''') +N' AS TableName,' + @CRLF +
STRING_AGG(N' COUNT_BIG(CASE WHEN ' + QUOTENAME(c.[name]) + N' IS NULL THEN 1 END) AS ' + QUOTENAME(c.[name]),@Delimiter) WITHIN GROUP(ORDER BY c.column_id) + @CRLF +
N' FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + N' T)' + @CRLF +
N'INSERT INTO #NullCounts(SchemaName, TableName, ColumnName, NULLCount)' + @CRLF +
N'SELECT SchemaName,' + @CRLF +
N' TableName,' + @CRLF +
N' V.ColumnName,' + @CRLF +
N' V.NULLCount' + @CRLF +
N'FROM Counts C' + @CRLF +
N' CROSS APPLY (VALUES' +
STUFF(STRING_AGG(N' (N' + QUOTENAME(c.[name], '''') + N', C.' + QUOTENAME(c.[name]) + N')',@Delimiter) WITHIN GROUP (ORDER BY c.column_id),1,24,N'') + N')V(ColumnName,NULLCount);'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
GROUP BY s.[name], t.[name]
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,4,N'');
--PRINT @SQL; --This is gunna be way longer than 4,000 characters, so you'll want SELECT
EXEC sys.sp_executesql @SQL;
GO
SELECT *
FROM #NullCounts
ORDER BY SchemaName,
TableName,
ColumnName;
GO
DROP TABLE #NullCounts;
是的,我混合了 STRING_AGG
和 FOR XML PATH
,是的,这很碍眼,但是打印的(选择的)SQL 产生了一些非常好的陈述。见下文:
WITH Counts AS(
SELECT N'dbo' AS SchemaName,
N'PerformanceTest' AS TableName,
COUNT_BIG(CASE WHEN TestID IS NULL THEN 1 END) AS [TestID],
COUNT_BIG(CASE WHEN TestTarget IS NULL THEN 1 END) AS [TestTarget],
COUNT_BIG(CASE WHEN TestName IS NULL THEN 1 END) AS [TestName],
COUNT_BIG(CASE WHEN TimeStart IS NULL THEN 1 END) AS [TimeStart],
COUNT_BIG(CASE WHEN TimeEnd IS NULL THEN 1 END) AS [TimeEnd],
COUNT_BIG(CASE WHEN TimeTaken_ms IS NULL THEN 1 END) AS [TimeTaken_ms],
COUNT_BIG(CASE WHEN TotalRows IS NULL THEN 1 END) AS [TotalRows],
COUNT_BIG(CASE WHEN RowSets IS NULL THEN 1 END) AS [RowSets],
COUNT_BIG(CASE WHEN AvgRowsPerSet IS NULL THEN 1 END) AS [AvgRowsPerSet]
FROM [dbo].[PerformanceTest] T)
INSERT INTO #NullCounts(SchemaName, TableName, ColumnName, NULLCount)
SELECT SchemaName,
TableName,
V.ColumnName,
V.NULLCount
FROM Counts C
CROSS APPLY (VALUES(N'TestID', C.[TestID]),
(N'TestTarget', C.[TestTarget]),
(N'TestName', C.[TestName]),
(N'TimeStart', C.[TimeStart]),
(N'TimeEnd', C.[TimeEnd]),
(N'TimeTaken_ms', C.[TimeTaken_ms]),
(N'TotalRows', C.[TotalRows]),
(N'RowSets', C.[RowSets]),
(N'AvgRowsPerSet', C.[AvgRowsPerSet]))V(ColumnName,NULLCount);
WITH Counts AS(
SELECT N'dbo' AS SchemaName,
N'someTable' AS TableName,
COUNT_BIG(CASE WHEN id IS NULL THEN 1 END) AS [id],
COUNT_BIG(CASE WHEN SomeCol IS NULL THEN 1 END) AS [SomeCol]
FROM [dbo].[someTable] T)
INSERT INTO #NullCounts(SchemaName, TableName, ColumnName, NULLCount)
SELECT SchemaName,
TableName,
V.ColumnName,
V.NULLCount
FROM Counts C
CROSS APPLY (VALUES(N'id', C.[id]),
(N'SomeCol', C.[SomeCol]))V(ColumnName,NULLCount);
是的,我真的花了最后 45 分钟写了所有这些...
老实说,这不是入门级的,如果你不了解它,你不应该使用它;而且,我非常怀疑您会找到一个不同的入门级解决方案,而且性能与此一样。 A CURSOR
,例如,虽然可能更容易理解,但这样做真的很慢。
警告: 如果您的数据库中有任何已弃用的数据类型(即 text
),这将失败。如果是这种情况,您将需要确保从 WHERE
中的查询中删除它们。但是,我建议您修复数据类型(text
,例如,已弃用 15 年)。
对于表格结果:
declare @sql nvarchar(max) =
(
select 'union all select (select object_schema_name('+ cast(tableobjectid as varchar(20))+') +''.''+ object_name('+ cast(tableobjectid as varchar(20))+') as "table/@name", count(*) as "table/@rowcount", ' + cols_concat + ' from ' + tablename + ' for xml path(''''), type)' as 'text()'
from
(
select
t.object_id as tableobjectid,
quotename(schema_name(t.schema_id)) + '.' + quotename(t.name) as tablename,
stuff( (select ', col_name(' + cast(c.object_id as varchar(20)) + ',' + cast(c.column_id as varchar(20)) + ') as "table/col/@name", count(*)-count('+case when type_name(c.system_type_id) in ('text', 'ntext', 'image') then ' case when ' + quotename(c.name) + ' is not null then 1 end' else quotename(c.name) end + ') as "table/col", null as "table"'
from sys.columns as c where c.object_id = t.object_id for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '') as cols_concat
from sys.tables as t
where t.is_ms_shipped = 0
) as tbl
for xml path(''), type).value('.', 'nvarchar(max)')
;
select @sql = 'select @xml = (select * from (select cast(null as xml) as "*" '+ @sql + ') as u for xml path('''') )';
declare @x xml;
set transaction isolation level read uncommitted;
exec sp_executesql @sql, N'@xml xml output', @xml = @x output;
set transaction isolation level read committed;
--shred
select
t.col.value('@name[1]', 'varchar(200)') as tablename,
t.col.value('@rowcount[1]', 'int') as tablerowcount,
r.col.value('@name[1]', 'varchar(200)') as columnname,
r.col.value('.[1]', 'int') as null_values
from @x.nodes('table') as t(col)
cross apply t.col.nodes('col') as r(col);
对于多个结果集(每个 table 一个):
declare @sql nvarchar(max) =
(
select 'select object_schema_name('+ cast(tableobjectid as varchar(20))+') +''.''+ object_name('+ cast(tableobjectid as varchar(20))+') as tablename, ' + cols_concat + ' from ' + tablename + ';' as 'text()'
from
(
select
t.object_id as tableobjectid,
quotename(schema_name(t.schema_id)) + '.' + quotename(t.name) as tablename,
stuff( (select ', count(*)-count('+case when type_name(c.system_type_id) in ('text', 'ntext', 'image') then ' case when ' + quotename(c.name) + ' is not null then 1 end' else quotename(c.name) end + ') as "NULL_' + replace(quotename(name), '"', '""')+'"' from sys.columns as c where c.object_id = t.object_id for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '') as cols_concat
from sys.tables as t
where t.is_ms_shipped = 0
) as tbl
for xml path(''), type).value('.', 'nvarchar(max)');
set transaction isolation level read uncommitted;
exec(@sql);
set transaction isolation level read committed;