#sql-server #loops #database-cursor
#sql-server #циклы #база данных-курсор
Вопрос:
У меня следующая проблема: более ранняя версия базы данных использовала значение » вместо NULL. Теперь я должен преобразовать эти значения везде, где они существуют, в NULL, если столбец имеет значение null.
Я попробовал следующий код:
BEGIN TRAN
DECLARE
@curcolumn VARCHAR(MAX),
@curtable VARCHAR(MAX);
DECLARE cursor_setnull CURSOR
FOR SELECT INFORMATION_SCHEMA.TABLES.TABLE_NAME, INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME FROM INFORMATION_SCHEMA.TABLES
JOIN INFORMATION_SCHEMA.COLUMNS ON INFORMATION_SCHEMA.TABLES.TABLE_NAME = INFORMATION_SCHEMA.COLUMNS.TABLE_NAME
WHERE INFORMATION_SCHEMA.COLUMNS.IS_NULLABLE = 'YES'
OPEN cursor_setnull
FETCH NEXT FROM cursor_setnull INTO
@curcolumn,
@curtable;
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM cursor_setnull INTO
@curcolumn,
@curtable;
DECLARE @numrows INT;
DECLARE @query NVARCHAR(MAX);
set @query = 'UPDATE ' @curtable ' SET ' @curcolumn ' = NULL WHERE ' @curcolumn ' = '''''
EXEC sp_executesql @query
END;
CLOSE cursor_setnull;
DEALLOCATE cursor_setnull;
ROLLBACK
При запуске он выдает следующие ошибки для каждого столбца: «Недопустимое имя объекта ‘column_name’.
Я был бы очень признателен, если бы кто-нибудь мог обнаружить ошибку для меня.
Комментарии:
1. Какая версия SQL Server?
2. Поскольку для этого требуется обновлять каждую строку несколько раз, это не очень хороший подход. Было бы лучше генерировать запросы, которые обновляют все столбцы одновременно. Я подозреваю, что Larnu запрашивает версию, чтобы точно знать
STRING_AGG
, доступна ли она для этой задачи.3. «Я подозреваю, что Larnu запрашивает версию, чтобы точно знать, доступна ли STRING_AGG для этой задачи» бинго, @JeroenMostert . Я предположил «да» из-за отсутствия противоположной информации (хотя я тоже демонстрирую, как использовать
FOR XML PATH
, из-за того, что не собираюсь вставлятьSTRING_AGG
).
Ответ №1:
Вместо того, чтобы использовать a CURSOR
, я бы предпочел делать все в пакете с некоторыми явными транзакциями. Я предполагаю, что SQL Server 2017 , из-за отсутствия ответа, когда я спросил, какую версию вы используете:
DECLARE @SQL nvarchar(MAX),
@CRLF nchar(2) = NCHAR(13) NCHAR(10);
DECLARE @Delimiter nvarchar(10) = N',' @CRLF N' '
SET @SQL = STUFF((SELECT @CRLF N'BEGIN TRANSACTION;' @CRLF
N' UPDATE ' QUOTENAME(s.[name]) N'.' QUOTENAME(t.[name]) @CRLF
N' SET ' (SELECT STRING_AGG(QUOTENAME(c.[name]) N' = CASE ' QUOTENAME(c.[name]) N' WHEN '''' THEN NULL ELSE ' QUOTENAME(c.[name]) N' END',@Delimiter) WITHIN GROUP (ORDER BY c.column_id)
FROM sys.columns c
JOIN sys.types ct ON c.system_type_id = ct.system_type_id
WHERE c.object_id = t.object_id
AND ct.[name] IN ('varchar','nvarchar','char','nchar')
AND c.is_nullable = 1
AND c.is_computed = 0) N';' @CRLF
N'COMMIT TRANSACTION'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.SCHEMA_ID
WHERE EXISTS (SELECT 1
FROM sys.columns c
JOIN sys.types ct ON c.system_type_id = ct.system_type_id
WHERE c.object_id = t.object_id
AND ct.[name] IN ('varchar','nvarchar','char','nchar')
AND c.is_nullable = 1
AND c.is_computed = 0)
ORDER BY s.schema_id, t.object_id
FOR XML PATH(N''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,2,N'');
PRINT @SQL; --Your best friend
--EXEC sys.sp_executesql @SQL; --Uncomment to run dynamic statement.
Комментарии:
1. Просто комментарий: приведение к NVARCHAR(MAX) внутри STRING_AGG-функции будет иметь дело с ошибкой при превышении 8000 бит, если кто-нибудь попытается это сделать.
2. Это означает, что у вас есть около 30 столбцов в одной таблице, определенных как строковый тип и обнуляемый, @RoyM .
![]()
3. Да, на самом деле у меня это есть в нескольких таблицах. Должно ли это вызывать какие-либо проблемы? Я не заметил никаких ошибок или ошибок после небольшого тестирования.
4. Это не вызывает проблем, нет, я хочу сказать, что это просто означает, что у вас есть таблицы, которые шире, чем я ожидал, причем большинство из них являются необязательными строковыми типами данных, @RoyM .
Ответ №2:
Если вы посмотрите на сгенерированные запросы, вы увидите, что запросы не соответствуют действительности.
Вам необходимо изменить порядок ввода значений в курсор.
FETCH NEXT FROM cursor_setnull INTO
@curcolumn,
@curtable;
должно быть
FETCH NEXT FROM cursor_setnull INTO
@curtable,
@curcolumn;
Комментарии:
1. Благодарим вас за обнаружение ошибки. Я ошибочно ввел это, играя с переменной @curtable, которая не «объявлена» во внутренней функции. Сначала я подумал, что отсутствует какая-то логика (; — или тому подобное), поэтому я переключил их, чтобы протестировать только логику синтаксического анализатора, и забыл об этом.