9

500 列と 1 億行の大きなテーブルがあります。小規模なサンプルに基づくと、値が含まれているのは約 50 列のみであり、残りの 450 列には NULL 値のみが含まれていると考えられます。データが含まれていない列を一覧表示したい。

現在のハードウェアでは、すべての列をクエリするのに約 24 時間かかります ( select count(1) from tab where col_n is not null)

列が完全に空/NULL であることを判断する安価な方法はありますか?

4

8 に答える 8

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 テーブル名

結果に値が含まれている場合、列に値が入力されます。1 つのテーブル スキャンが必要です。

于 2013-05-31T07:57:13.250 に答える
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
于 2013-05-31T07:35:02.400 に答える
0

ほとんどのレコードが null でない場合は、提案されたアプローチのいくつか (たとえば、null 可能なフィールドのみをチェックする) をこれと組み合わせることができます。

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

条件が満たされるとすぐに exists が検索を停止するため、これにより検索が高速化されます。この例では、フィールドのステータスを決定するには、null でないレコードが 1 つあれば十分です。フィールドにインデックスがある場合、これはほぼ瞬時に行われます。

通常、このクエリに上位 1 を追加する必要はありません。クエリ オプティマイザーは、一致するすべてのレコードを取得する必要がないことを認識しているためです。

于 2015-02-11T10:47:20.530 に答える
0

このストアド プロシージャをトリックに使用できます。クエリを実行するテーブル名を指定する必要があります。プロシージャに @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) = '['+@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
于 2015-05-15T17:58:46.550 に答える
0

100M レコードすべてを「カウント」する必要はありません。null 以外の値を持つ列にヒットするとすぐに TOP 1 を使用してクエリを単純に取り消すと、同じ情報を提供しながら多くの時間を節約できます。

于 2013-05-31T08:55:42.043 に答える
0

データ型、NOT NULL、および PRIMARY KEY 制約と共にテーブル内の列のリストを取得するための SQL サーバー クエリ

上記の質問の最良の回答で SQL を実行し、以下のような新しいクエリを生成します。

Select ISNULL(column1,1), ISNULL(column2,1), ISNULL(column3,1) from table

于 2013-05-31T07:44:07.950 に答える
0

500列?!
わかりました、あなたの質問に対する正しい答えは、テーブルを正規化することです。

当面の間、次のことが起こっています。

その列にはインデックスがないため、SQL Server は巨大なテーブルのフル スキャンを実行する必要があります。
SQL Server は確実にすべての行を完全に読み取ります (1 つの列だけに関心がある場合でも、すべての列を意味します)。
そして、あなたの行はおそらく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
于 2013-05-31T08:30:50.157 に答える