Установка NULL для всех столбцов с нулевым значением в SQL

#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, которая не «объявлена» во внутренней функции. Сначала я подумал, что отсутствует какая-то логика (; — или тому подобное), поэтому я переключил их, чтобы протестировать только логику синтаксического анализатора, и забыл об этом.