我有一個500列和100M行的大桌子。基於一個小樣本,我相信只有大約50個列包含任何值,另外450個只包含NULL值。我想列出不包含數據的列。如何廉價確定列是否僅包含NULL記錄?
在我當前的硬件,這將需要大約24小時查詢每列(select count(1) from tab where col_n is not null
)
有沒有更便宜的方法來確定一個列是完全空/空?
我有一個500列和100M行的大桌子。基於一個小樣本,我相信只有大約50個列包含任何值,另外450個只包含NULL值。我想列出不包含數據的列。如何廉價確定列是否僅包含NULL記錄?
在我當前的硬件,這將需要大約24小時查詢每列(select count(1) from tab where col_n is not null
)
有沒有更便宜的方法來確定一個列是完全空/空?
這個怎麼樣:
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表名
如果結果中包含值,列填充。它應該需要一個表掃描。
我最關心的是性能,而不是生成查詢。 –
+1。完全同意這個答案。 – Devart
@ user2161466請注意,這個答案不僅僅是查詢創建,它允許您一次處理所有列,而不是逐個處理它們。這可能意味着巨大的業績。 –
嘗試這一個 -
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
在單列上,這給出了相同的性能。你期望在總體上有更好的表現嗎? select dbo.tab其中col不爲空 - 1:07 SELECT col = COUNT(CASE WHEN ISNULL(CAST(col AS NVARCHAR(MAX)),'')=''THEN 1 END) FROM dbo。標籤 - 1:08 –
我(海報)沒有投票。 –
@Devart:我不是downvoter,但爲什麼這更好的一些解釋會很有趣。總是最好不要只給出答案,而是幫助人們學習。 :) – Chris
你能檢查是否colums idexing意志幫助你達到一定的性能提升
CREATE UNIQUE NONCLUSTERED INDEX IndexName ON dbo.TableName(ColumnName)
WHERE ColumnName IS NOT NULL;
GO
我考慮過這個問題,但是需要儘可能在列上創建索引,因爲它會查詢它爲空值。 –
但還是有好處的。你只索引一次,但你可以查詢多次,只要你願意 –
我將不得不在每一列上創建索引,對吧?我在我的問題中忽略了一些背景,那就是我需要這個過程在表格的未索引版本上重複。 –
運行SQL中的上述問題最好的答案,併產生類似下面的新查詢。
Select ISNULL(column1,1), ISNULL(column2,1), ISNULL(column3,1) from table
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
你不會需要「數'所有的100M記錄。當您用一個非空值打開一列時,只要簡單地退出查詢,就可以在提供相同信息的同時節省大量時間。
如果大多數記錄不是空也許你可以把一些該方法的建議(例如檢查只可空字段)與此:
if exists (select * from table where field is not null)
因爲存在這應該加快搜索就停止搜索當條件滿足時,在這個例子中,一個非空的記錄足以決定該字段的狀態。 如果該字段有索引,這應該幾乎是即時的。
通常將top 1添加到此查詢不是必需的,因爲查詢優化器知道您不需要檢索所有匹配的記錄。
您可以使用此存儲過程的伎倆,您需要提供您想查詢請注意,如果你傳遞給程序@exec參數= 1,將執行SELECT查詢
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) = '['[email protected]+']'
CREATE TABLE #table (ColumnName VARCHAR (max))
SELECT @i+=
' IF EXISTS (SELECT TOP 1 '+column_name+' FROM ' [email protected]+' WHERE ' +column_name+
' '+'IS NOT NULL) INSERT INTO #table VALUES ('''+column_name+''');'
FROM INFORMATION_SCHEMA.COLUMNS WHERE [email protected]
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 ' [email protected]
END
ELSE
BEGIN
SELECT @SELECT= 'SELECT '+ LTRIM (left (@sentence,NULLIF(LEN (@sentence)-1,-1)))+
+' FROM '[email protected]
EXEC (@SELECT)
END
END
END
表名
使用方法如下:
EXEC [dbo].[SP_SELECT_NON_NULL_COLUMNS] 'YourTableName' , 1
將列以某種方式進行更新?是否允許更改? –
列不需要更新。它們不能作爲解決方案的一部分進行修改。 –
主要關注的是性能,不寫動態sql來生成查詢。 –