2013-05-31 24 views
6

Tôi có một bảng lớn với 500 cột và 100M hàng. Dựa trên một mẫu nhỏ, tôi tin rằng chỉ có khoảng 50 cột có chứa bất kỳ giá trị nào, và 450 khác chỉ chứa giá trị NULL. Tôi muốn liệt kê các cột không chứa dữ liệu.Làm thế nào tôi có thể xác định chi phí nếu một cột chỉ chứa các bản ghi NULL?

Mở phần cứng hiện tại của tôi, nó sẽ mất khoảng 24 giờ để truy vấn mỗi cột (select count(1) from tab where col_n is not null)

Có cách nào ít tốn kém để xác định rằng một cột là hoàn toàn trống rỗng/NULL?

+0

Các cột có được cập nhật theo một cách nào đó không? Các thay đổi có được phép không? –

+0

Các cột không cần phải được cập nhật. Chúng không thể được sửa đổi như là một phần của giải pháp. –

+0

Mối quan tâm chính là hiệu suất, không viết sql động để tạo truy vấn. –

Trả lời

13

gì về điều này:

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 

?

Bạn có thể dễ dàng tạo truy vấn này nếu bạn sử dụng bảng INFORMATION_SCHEMA.COLUMNS.

EDIT:

Một ý tưởng:

CHỌN MAX (column_1), MAX (column_2), ..... FROM tên_bảng

Nếu kết quả chứa giá trị, cột được dân cư. Nó sẽ yêu cầu quét một bảng.

+0

Mối quan tâm chính của tôi là hiệu suất, không tạo truy vấn. –

+0

+1. Hoàn toàn đồng ý với câu trả lời này. – Devart

+1

@ user2161466 Lưu ý rằng câu trả lời này không chỉ là về tạo truy vấn, nó cho phép bạn xử lý tất cả các cột cùng một lúc thay vì đi qua từng cột một. Điều đó có thể có nghĩa là một cải tiến hiệu suất rất lớn. –

1

Hãy thử điều này một -

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) 

Query 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 

Output 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 

Query 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 

Output 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 

Kết quả:

ID   Name  IsCitizen Age 
----------- ----------- ----------- ----------- 
0   0   0   1 
+0

Trên một cột đơn, điều này mang lại hiệu suất tương tự. Bạn có mong đợi hiệu suất tốt hơn trong tổng hợp không? chọn số (1) từ dbo.tab trong đó col không phải là rỗng - 1:07 CHỌN col = COUNT (TRƯỜNG HỢP KHI ISNULL (CAST (col AS NVARCHAR (MAX)), '') = '' THEN 1 END) TỪ dbo.tab - 1:08 –

+0

Tôi (người đăng) không bỏ phiếu này. –

+1

@Devart: Tôi không phải là người giải quyết nhưng một số giải thích về lý do tại sao điều này là tốt hơn sẽ là thú vị. Luôn luôn tốt nhất không chỉ để đưa ra câu trả lời mà còn giúp mọi người học hỏi. :) – Chris

0

bạn có thể kiểm tra xem colums idexing chí giúp bạn đạt được một số hiệu suất cải thiện

CREATE UNIQUE NONCLUSTERED INDEX IndexName ON dbo.TableName(ColumnName) 
WHERE ColumnName IS NOT NULL; 
GO 
+0

Tôi đã xem xét điều này, nhưng phải mất bao lâu để tạo một chỉ mục trên một cột như nó để truy vấn nó cho các giá trị rỗng. –

+0

Nhưng vẫn có một lợi ích. bạn đang lập chỉ mục một lần, nhưng bạn có thể truy vấn nhiều lần như bạn muốn –

+0

Tôi sẽ phải tạo một chỉ mục trên mỗi cột mặc dù, phải không? Tôi đã bỏ qua một số ngữ cảnh trong câu hỏi của mình, đó là tôi cần quá trình này lặp lại được trên một phiên bản chưa được lập chỉ mục của bảng. –

0

500 Cột ?!
Ok, câu trả lời đúng cho câu hỏi của bạn là: bình thường hóa bảng của bạn.

Đây là những gì xảy ra cho thời gian được:

Bạn không có một chỉ mục trên cột đó để SQL Server đã làm một quét toàn bộ bảng khổng lồ của mình.
SQL Server chắc chắn sẽ đọc toàn bộ hàng (nghĩa là mọi cột ngay cả khi bạn chỉ quan tâm đến một cột).
Và kể từ khi hàng của bạn có nhiều khả năng hơn 8KB ... http://msdn.microsoft.com/en-us/library/ms186981%28v=sql.105%29.aspx

Nghiêm túc, bình thường hóa bảng của bạn và nếu cần chia nó theo chiều ngang (đặt "chủ đề nhóm" cột bên trong bảng riêng biệt, để chỉ đọc chúng khi bạn cần đến chúng) .

EDIT: Bạn có thể viết lại truy vấn của bạn như thế này

select count(col_n) from tab 

và nếu bạn muốn để có được tất cả các cột cùng một lúc (tốt hơn):

SELECT 
    COUNT(column_1) column_1_count, 
    COUNT(column_2) column_2_count, 
    ... 
FROM table_name 
+1

Nếu nó có 100 triệu bản ghi, có vẻ như tôi đã sử dụng sản phẩm. Tái cấu trúc không phải lúc nào cũng dễ dàng trong trường hợp đó. – Mr47

+0

Thỉnh thoảng bạn phải thực hiện các thay đổi quyết liệt để giữ cho hệ thống có thể sử dụng được. – Serge

+0

Đây là bảng do nhà cung cấp dữ liệu cung cấp theo định kỳ. Mã tôi đang viết là dọn dẹp nó. –

0

Bạn sẽ không cần phải 'đếm 'tất cả các bản ghi 100 triệu. Khi bạn chỉ cần thoát khỏi truy vấn bằng TOP 1 ngay sau khi bạn nhấn một cột có giá trị không null, sẽ tiết kiệm rất nhiều thời gian trong khi cung cấp cùng một thông tin.

0

Nếu hầu hết các hồ sơ không phải là null có thể bạn có thể kết hợp một số phương pháp tiếp cận đề nghị (ví dụ kiểm tra chỉ lĩnh vực nullable) với điều này:

if exists (select * from table where field is not null) 

này cần đẩy mạnh việc tìm kiếm vì tồn tại dừng việc tìm kiếm càng sớm khi điều kiện được đáp ứng, trong ví dụ này, một bản ghi không đơn lẻ là đủ để quyết định trạng thái của trường. Nếu trường có chỉ mục, điều này sẽ gần như ngay lập tức.

Thông thường thêm từ đầu 1 vào truy vấn này là không cần thiết vì trình tối ưu hóa truy vấn biết rằng bạn không cần truy xuất tất cả các bản ghi khớp.

0

Bạn có thể sử dụng thủ tục lưu trữ này để lừa Bạn cần cung cấp tên bảng bạn muốn truy vấn lưu ý rằng nếu bạn sẽ vượt qua thủ tục các tham số @exec = 1 nó sẽ thực hiện truy vấn chọn

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 

Sử dụng công cụ như sau:

EXEC [dbo].[SP_SELECT_NON_NULL_COLUMNS] 'YourTableName' , 1 
Các vấn đề liên quan