删除所有列(ID除外)为NULL的行

时间:2019-10-10 10:16:28

标签: sql sql-server

我有一些带有100-300列的大表,有时一个项目充满了空值,只留下了ID,就像这样:

|---------------------|------------------|------------------|     |------------------|
|          ID         |     Column 1     |     Column 2     | ....|    Column 300    |
|---------------------|------------------|------------------|     |----------------- |
|          1          |      value       |       value      | ....
|---------------------|------------------|------------------|
|          2          |       NULL       |        NULL      | ....
|---------------------|------------------|------------------|
|          3          |       NULL       |        NULL      | ....
|---------------------|------------------|------------------|

所以我想删除那些,但我能想到的唯一方法是很大一部分
 CASE (colA IS NULL and colB IS NULL AND colC IS NULL ...) 但这对我来说是不切实际的,因为有很多表和很多列。

是否有一种方法可以删除除ID列之外仅包含NULL的每一行?

5 个答案:

答案 0 :(得分:2)

只需替换为您的表和架构:

Pre-request Script

在上面的示例中,我们获取主键列(如果存在),以便不将其包含在DECLARE @TableSchema SYSNAME ,@TableName SYSNAME SELECT @TableSchema = 'dbo' ,@TableName = 'SurveyInstances'; DECLARE @DynamicTSQLStatement NVARCHAR(MAX); SET @DynamicTSQLStatement = 'DELETE FROM ' + @TableSchema + '.' + @TableName + ' WHERE ' + STUFF ( ( SELECT ' AND ' + [name] + ' IS NULL' FROM [sys].[columns] WHERE [object_id] = OBJECT_ID(@TableSchema + '.' + @TableName) AND [column_id] NOT IN ( SELECT IC.[column_id] FROM [sys].[indexes] I INNER JOIN [sys].[index_columns] IC ON I.[object_id] = IC.[object_id] AND I.[index_id] = IC.[index_id] WHERE I.[is_primary_key] = 1 AND I.[object_id] = OBJECT_ID(@TableSchema + '.' + @TableName) ) FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1 ,5 ,'' ); EXEC sp_executesql @DynamicTSQLStatement; 子句中。然后有了这些列,只需连接WHERE语句,您的T-SQL语句就准备好了。

答案 1 :(得分:2)

到目前为止,这里还没有SQL专家,但是您可以将表转换为XML,将所有NULL值列标记为nil-elements,然后过滤出除列id之外仅包含nil-elements的所有行。

--Glorious test table with an amazing amount of three columns..
declare @tTable table ([id] int, [c1] varchar(10), [c2] bit);
insert into @tTable values
    (1, 'wayne', null),
    (2, null, 1),
    (3, null, null), --This one goes
    (4, null, 0),
    (5, '', null),
    (7, null, null); --This one goes;

--Transform table to XML marking NULL values with @nil..
declare @tXML xml; set @tXML = (
    select
        *
    from
        @tTable
    for xml path('row'), type, elements xsinil
);

--Removes all [id] contained in the select below..
delete from @tTable where [id] in (
    --Select all [id] which have no element NOT being marked as NULL (=merely NULL values)
    select
        p.value('./id[1]', 'int')
    from
        (select 1 as [wayne]) as [tT]
        cross apply @tXML.nodes('/row') as t(p)
    where
        (
            p.exist('./*[not(local-name(.)="id")][not(@xsi:nil)]') = 0
        )
);

select * from @tTable;

答案 2 :(得分:1)

有一种方法可以在没有动态SQL的情况下,也不需要对ID以外的所有值均为NULL的列进行任何手动选择:

  • 您说其他所有列,但id都可以为空
  • 因此我们仅将值-666的ID插入源表中
    由于除id外的所有字段均为NULLABLE,因此这很容易。
    由于autoid创建的值> 0,因此在-666上不会发生任何冲突
  • 我们在临时表中选择-666 id
  • 我们现在可以从源表中删除-666条目
  • 我们将id列放在临时表中
  • 我们将临时表交叉连接到源表
  • 我们从源表中选择ID,并从临时表中选择所有字段(空字段)
  • 我们将此null-value-table-with-id与源表相交
  • 我们从相交表中选择所有ID
  • 然后我们通过这些ID从源表中删除行
  • 为确保临时表存在(连接池)不会出错,我们在临时表的开头和结尾都将其删除(如果存在)。
  • 完成
  • 注意:如果将主键(id)指定为身份(auto_increment),则需要在插入语句之前和之后转动IDENTITY INSERT ON/OFF


IF OBJECT_ID('tempdb..#a') IS NOT NULL DROP TABLE #a; 

DELETE FROM Foobar WHERE id = -666; 
SET IDENTITY_INSERT dbo.Foobar ON; -- only if the id field is an IDENTITY
INSERT INTO Foobar(id) SELECT -666 AS id; 
SET IDENTITY_INSERT dbo.Foobar OFF; -- only if the id field is an IDENTITY


SELECT * 
INTO #a 
FROM Foobar 
WHERE Foobar.id = -666;

ALTER TABLE #a DROP COLUMN id; 
DELETE FROM Foobar WHERE id = -666; 

DELETE FROM Foobar WHERE Foobar.id IN 
(
    SELECT tIntersect.id FROM 
    (
        SELECT * FROM Foobar 

        INTERSECT 

        SELECT 
             Foobar.id 
            ,tNullValues.* 
        FROM Foobar 
        CROSS JOIN #a AS tNullValues 
    ) AS tIntersect  
); 

IF OBJECT_ID('tempdb..#a') IS NOT NULL DROP TABLE #a; 

请注意,如果您具有xml,text,geoge或architectureid类型的列,则相交将失败。另外,请注意,SQL-server并未实现INTERSECT ALL,因此只有在表具有主键的情况下(仅当您至少具有一个具有唯一ID的不可空列时,这才可靠地工作-主键可确保但该列不必一定要定义为主键)。





旧的更复杂的变体:

您可以使用INTERSECT来做到这一点:

示例:

CREATE TABLE dbo.Foobar
(
    id int NOT NULL,
    nam varchar(50) NULL
)

输入一些具有ID和名称的值,以及一些仅具有ID的值

然后做:

DECLARE @maxId as integer 
SET @maxId = (SELECT MAX(id) FROM Foobar);


;WITH CTE AS 
(
    SELECT 1 AS i 
    UNION ALL 
    SELECT i+1 AS i 
    FROM CTE 
    WHERE CTE.i < @maxId 
)



SELECT 
     id 
    ,nam 
FROM Foobar 

INTERSECT 

SELECT 
     i AS id 
    ,CAST(NULL AS varchar(50)) AS nam 
FROM CTE 
OPTION (MAXRECURSION 0) 

这将产生要删除的所有行的id值。

然后您可以执行以下操作:

DECLARE @maxId as integer 
SET @maxId = (SELECT MAX(id) FROM Foobar);


;WITH CTE AS 
(
    SELECT 1 AS i 
    UNION ALL 
    SELECT i+1 AS i 
    FROM CTE 
    WHERE CTE.i < @maxId 
)


DELETE FROM Foobar WHERE id IN 
(
    SELECT id FROM 
    (
        SELECT 
             id 
            ,nam 
        FROM Foobar 

        INTERSECT 

        SELECT 
             i AS id 
            ,CAST(NULL AS varchar(50)) AS nam 
        FROM CTE 
    ) AS t 
)
OPTION (MAXRECURSION 0) 

或者您可以动态生成列列表:

SELECT 
    CASE 
        WHEN ORDINAL_POSITION = 1 THEN ' CAST(NULL AS ' + DATA_TYPE + ') AS ' + QUOTENAME(COLUMN_NAME) 
        ELSE ',CAST(NULL AS ' + DATA_TYPE + ') AS ' + QUOTENAME(COLUMN_NAME) 
    END 
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME = 'Foobar' 
AND TABLE_SCHEMA = 'dbo'

然后使用它,您可以获得主键列,因此可以从列列表中排除它们:

SELECT kcu.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS tc 
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu 
    ON kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME 
    AND kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA 
    AND kcu.TABLE_SCHEMA = tc.TABLE_SCHEMA 
    AND kcu.TABLE_NAME = tc.TABLE_NAME 

WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
AND tc.TABLE_SCHEMA = 'dbo' 
AND tc.TABLE_NAME = 'Foobar'

答案 3 :(得分:1)

这是我“欺骗”引擎执行此操作的方法:

1)手动查找所有列均为空的ID。假设您发现匹配的ID为56。该表的所有行(ID除外)都应为null。将其放入模板:

select * 
into #a
from yourtable 
where ID=56

2)从该表中删除ID列。仅保留空列。

alter table #a drop column ID

3)通过使用id和临时表的笛卡尔积来创建所有可能不必要的行。 然后,使用EXCEPT将其从原始表中删除:

    select * from yourtable
except
    select t.ID,#a.*
        from yourtable t
        cross join #a

答案 4 :(得分:1)

您可以根据需要在查询下面运行。它将删除所有具有NULL值(ID除外)COLUMN的行

DECLARE @TSchema SYSNAME
       ,@TName SYSNAME

SELECT @TSchema = 'dbo'
      ,@TName = 'yourTableName';

DECLARE @TSQLStatement NVARCHAR(MAX);

SET @TSQLStatement = 'DELETE FROM '  + @TSchema + '.' + @TName + ' WHERE ' +  STUFF
(
    (
        SELECT ' AND ' + [name] + ' IS NULL' 
        FROM [sys].[columns] 
        WHERE [object_id] = OBJECT_ID(@TSchema + '.' + @TName)
            AND [column_id] NOT IN
            (
                SELECT IC.[column_id] FROM [sys].[indexes] I
                INNER JOIN [sys].[index_columns] IC ON I.[object_id] = IC.[object_id]
             AND I.[index_id] = IC.[index_id]
                WHERE I.[type] = 1
                    AND I.[object_id] =  OBJECT_ID(@TSchema + '.' + @TName)
            )AND name <> 'id'
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)')
    ,1
    ,5
    ,''
);

EXEC sp_executesql @TSQLStatement;

免费查询。