Есть способ сделать это без динамического 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'