Удалить строки, в которых все столбцы (кроме ID) имеют значение NULL - PullRequest
0 голосов
/ 10 октября 2019

У меня есть несколько больших таблиц с 100-300 столбцами, и иногда элемент полон пустых значений, оставляя только идентификатор, например:

|---------------------|------------------|------------------|     |------------------|
|          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, содержит только NULLS?

Ответы [ 5 ]

3 голосов
/ 10 октября 2019

Просто замените вашу таблицу и схему:

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. Затем, имея столбцы, просто объедините операторы IS NULL, и ваш оператор T-SQL готов.

2 голосов
/ 11 октября 2019

Безусловно, здесь нет специалиста по SQL, но вы можете преобразовать таблицу в XML, пометить все столбцы, имеющие значение NULL, как nil-элементы, а затем отфильтровать все строки, в которых есть только nil-элементы, кроме идентификатора столбца.

--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;
1 голос
/ 10 октября 2019

Вы можете просто запустить ниже запрос для вашего требования. Он удалит все строки, имеющие значения NULL (кроме идентификатора). 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;

Не стесняйтесь для любого запроса.

1 голос
/ 10 октября 2019

Вот мой способ «обмануть» движок, чтобы сделать это:

1) Вручную найти идентификатор со всеми столбцами нулевыми. Допустим, вы нашли хиты с ID = 56. Все строки этой таблицы, кроме ID, должны иметь значения null. Поместите это в temptable:

select * 
into #a
from yourtable 
where ID=56

2) Удалите столбец ID из этой таблицы. Остаются только пустые столбцы.

alter table #a drop column ID

3) Создайте все возможно ненужные строки, используя декартово произведение идентификаторов и временную таблицу. Затем удалите это из исходной таблицы, используя EXCEPT:

    select * from yourtable
except
    select t.ID,#a.*
        from yourtable t
        cross join #a
1 голос
/ 10 октября 2019

Есть способ сделать это без динамического SQL и без ручного выбора столбца, в котором все значения, кроме id, равны NULL:

  • вы сказали, что все остальные столбцы, кроме id, могут быть обнулены
  • , поэтому мы вставляем в исходную таблицу только идентификатор со значением -666поскольку все поля, кроме id, имеют значение NULLABLE, это легко. поскольку autoids создает значения> 0, вы не столкнетесь с -666
  • , мы выбрали идентификатор -666 во временной таблице
  • , теперь мы можем удалить запись -666 из источникаtable
  • мы опускаем столбец id во временную вкладку
  • мы пересекаем соединение временной таблицы с исходной таблицей
  • мы выбираем id из исходной таблицы и всеполя (нулевые поля) из временной таблицы
  • мы пересекаем эту таблицу нулевых значений с идентификатором источника с таблицей
  • выбираем все идентификаторы из таблицы пересечений
  • и затем мы удаляем строки из исходной таблицы по этим идентификаторам
  • , чтобы убедиться, что мы не получим ошибку, если временная таблица существует (пул соединений), мы удаляем временную таблицу, если она уже существуетсуществует как в начале, так и в конце.
  • done
  • Примечание: если ваш первичный ключ (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, geography илиierarchyid. Также обратите внимание, что SQL-сервер не реализует INTERSECT ALL, поэтому он работает надежно только в том случае, если у вашей таблицы есть первичный ключ (соответственно, только если у вас есть хотя бы один столбец со значением NULL с уникальным идентификатором - первичный ключ гарантирует, что,но столбец не обязательно должен быть определен как первичный ключ).

Старый, более сложный вариант: Вы можете сделать это с помощью INTERSECT:

Пример:

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

Введите несколько значений с идентификатором и именем, а некоторые с просто идентификаторами

Затем выполните:

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) 

Получает значения идентификаторов всех строк, которые вы хотите удалить.

Тогда вы можете сделать это:

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'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...