我有一个包含500列和100M行的大表。基于一个小样本,我相信只有大约50个列包含任何值,而另一个450只包含NULL值。我想列出不包含数据的列。
在我目前的硬件上,查询每列(select count(1) from tab where col_n is not null
)
是否有一种较便宜的方法来确定列是否完全为空/ NULL?
答案 0 :(得分:14)
这个怎么样:
SELECT
SUM(CASE WHEN column_1 IS NOT NULL THEN 1 ELSE 0) column_1_count,
SUM(CASE WHEN column_2 IS NOT NULL THEN 1 ELSE 0) column_2_count,
...
FROM table_name
如果使用INFORMATION_SCHEMA.COLUMNS表,则可以轻松创建此查询。
修改强>
另一个想法:
SELECT MAX(column_1),MAX(column_2),..... FROM table_name
如果结果包含值,则填充列。它应该需要一次表扫描。
答案 1 :(得分:1)
试试这个 -
<强> DDL:强>
IF OBJECT_ID ('dbo.test2') IS NOT NULL
DROP TABLE dbo.test2
CREATE TABLE dbo.test2
(
ID BIGINT IDENTITY(1,1) PRIMARY KEY
, Name VARCHAR(10) NOT NULL
, IsCitizen BIT NULL
, Age INT NULL
)
INSERT INTO dbo.test2 (Name, IsCitizen, Age)
VALUES
('1', 1, NULL),
('2', 0, NULL),
('3', NULL, NULL)
查询1:
DECLARE
@TableName SYSNAME
, @ObjectID INT
, @SQL NVARCHAR(MAX)
SELECT
@TableName = 'dbo.test2'
, @ObjectID = OBJECT_ID(@TableName)
SELECT @SQL = 'SELECT' + CHAR(13) + STUFF((
SELECT CHAR(13) + ', [' + c.name + '] = ' +
CASE WHEN c.is_nullable = 0
THEN '0'
ELSE 'CASE WHEN ' + totalrows +
' = SUM(CASE WHEN [' + c.name + '] IS NULL THEN 1 ELSE 0 END) THEN 1 ELSE 0 END'
END
FROM sys.columns c WITH (NOWAIT)
CROSS JOIN (
SELECT totalrows = CAST(MIN(p.[rows]) AS VARCHAR(50))
FROM sys.partitions p
WHERE p.[object_id] = @ObjectID
AND p.index_id IN (0, 1)
) r
WHERE c.[object_id] = @ObjectID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ') + CHAR(13) + 'FROM ' + @TableName
PRINT @SQL
EXEC sys.sp_executesql @SQL
输出1:
SELECT
[ID] = 0
, [Name] = 0
, [IsCitizen] = CASE WHEN 3 = SUM(CASE WHEN [IsCitizen] IS NULL THEN 1 ELSE 0 END) THEN 1 ELSE 0 END
, [Age] = CASE WHEN 3 = SUM(CASE WHEN [Age] IS NULL THEN 1 ELSE 0 END) THEN 1 ELSE 0 END
FROM dbo.test2
查询2:
DECLARE
@TableName SYSNAME
, @SQL NVARCHAR(MAX)
SELECT @TableName = 'dbo.test2'
SELECT @SQL = 'SELECT' + CHAR(13) + STUFF((
SELECT CHAR(13) + ', [' + c.name + '] = ' +
CASE WHEN c.is_nullable = 0
THEN '0'
ELSE 'CASE WHEN '+
'MAX(CAST([' + c.name + '] AS CHAR(1))) IS NULL THEN 1 ELSE 0 END'
END
FROM sys.columns c WITH (NOWAIT)
WHERE c.[object_id] = OBJECT_ID(@TableName)
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ') + CHAR(13) + 'FROM ' + @TableName
PRINT @SQL
EXEC sys.sp_executesql @SQL
输出2:
SELECT
[ID] = 0
, [Name] = 0
, [IsCitizen] = CASE WHEN MAX(CAST([IsCitizen] AS CHAR(1))) IS NULL THEN 1 ELSE 0 END
, [Age] = CASE WHEN MAX(CAST([Age] AS CHAR(1))) IS NULL THEN 1 ELSE 0 END
FROM dbo.test2
<强>结果:强>
ID Name IsCitizen Age
----------- ----------- ----------- -----------
0 0 0 1
答案 2 :(得分:0)
您能否检查列索引是否有助于您达到某些性能提升
CREATE UNIQUE NONCLUSTERED INDEX IndexName ON dbo.TableName(ColumnName)
WHERE ColumnName IS NOT NULL;
GO
答案 3 :(得分:0)
在上述问题的最佳答案中运行SQL并生成如下所示的新查询。
Select ISNULL(column1,1), ISNULL(column2,1), ISNULL(column3,1) from table
答案 4 :(得分:0)
500列?!
好的,对你的问题的正确答案是:将你的桌子标准化。
以下是目前发生的事情:
您没有该列的索引,因此SQL Server必须对您的庞大表进行全面扫描。
SQL Server肯定会完全读取每一行(即使你只对一行感兴趣,它也意味着每一列)。
因为你的行最有可能超过8kb ...... http://msdn.microsoft.com/en-us/library/ms186981%28v=sql.105%29.aspx
严重的是,规范化你的表格,如果需要,将其水平分割(将“主题分组”列放在单独的表中,只在需要时才读取它们。)
编辑:您可以像这样重写您的查询
select count(col_n) from tab
如果你想一次获得所有列(更好):
SELECT
COUNT(column_1) column_1_count,
COUNT(column_2) column_2_count,
...
FROM table_name
答案 5 :(得分:0)
您不需要“计算”所有100M记录。当您点击具有非空值的列时,只需使用TOP 1退出查询,在提供相同信息时将节省大量时间。
答案 6 :(得分:0)
如果大多数记录都不为null,那么你可以将一些建议的方法(例如只检查可空字段)混合使用:
if exists (select * from table where field is not null)
这应该加快搜索速度,因为只要满足条件,存在就会停止搜索,在这个例子中,单个非空记录足以决定字段的状态。 如果该字段有一个索引,那么这几乎应该是即时的。
通常不需要在此查询中添加前1,因为查询优化器知道您不需要检索所有匹配的记录。
答案 7 :(得分:0)
您可以使用此存储过程来获取技巧您需要提供要查询的表名,请注意如果您将传递给过程@exec参数= 1,它将执行选择查询
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[SP_SELECT_NON_NULL_COLUMNS] ( @tablename varchar (100)=null, @exec int =0)
AS BEGIN
SET NOCOUNT ON
IF @tablename IS NULL
RAISERROR('CANT EXECUTE THE PROC, TABLE NAME IS MISSING',16 ,1)
ELSE
BEGIN
IF OBJECT_ID('tempdb..#table') IS NOT NULL DROP TABLE #table
DECLARE @i VARCHAR (max)=''
DECLARE @sentence VARCHAR (max)=''
DECLARE @SELECT VARCHAR (max)
DECLARE @LocalTableName VARCHAR(50) = '['+@tablename+']'
CREATE TABLE #table (ColumnName VARCHAR (max))
SELECT @i+=
' IF EXISTS ( SELECT TOP 1 '+column_name+' FROM ' +@LocalTableName+' WHERE ' +column_name+
' '+'IS NOT NULL) INSERT INTO #table VALUES ('''+column_name+''');'
FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=@tablename
INSERT INTO #table
EXEC (@i)
SELECT @sentence = @sentence+' '+columnname+' ,' FROM #table
DROP TABLE #table
IF @exec=0
BEGIN
SELECT 'SELECT '+ LTRIM (left (@sentence,NULLIF(LEN (@sentence)-1,-1)))+
+' FROM ' +@LocalTableName
END
ELSE
BEGIN
SELECT @SELECT= 'SELECT '+ LTRIM (left (@sentence,NULLIF(LEN (@sentence)-1,-1)))+
+' FROM '+@LocalTableName
EXEC (@SELECT)
END
END
END
像这样使用:
EXEC [dbo].[SP_SELECT_NON_NULL_COLUMNS] 'YourTableName' , 1