【问题标题】:Setting NULL for all nullable columns in SQL为 SQL 中的所有可空列设置 NULL
【发布时间】:2020-11-02 10:51:26
【问题描述】:

我有以下问题:早期版本的数据库使用值 '' 而不是 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'。

如果有人能帮我发现错误,我将不胜感激。

【问题讨论】:

  • 什么版本的 SQL Server?
  • 因为这需要多次更新每一行,这不是一个很好的方法。生成一次更新所有列的查询会更好。我怀疑 Larnu 要求的版本正是为了知道 STRING_AGG 是否可用于此任务。
  • “我怀疑 Larnu 要求的版本正是为了知道 STRING_AGG 是否可用于此任务” 宾果游戏,@JeroenMostert。由于缺乏相反的信息,我假设“是”(尽管我也演示了如何使用 FOR XML PATH,因为不打算嵌套 STRING_AGG)。

标签: sql-server loops database-cursor


【解决方案1】:

与其使用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.

【讨论】:

  • 只是评论:如果有人尝试这样做,则在 STRING_AGG 函数内转换为 NVARCHAR(MAX) 将处理超过 8000 位的错误。
  • 确实 意味着您在一个定义为字符串类型的单个表中有大约 30 多列,并且可以为空,@RoyM。 :)
  • 是的,事实上我在多个表中都有。它应该引起任何问题吗?经过一段时间的测试,我没有发现任何错误或错误。
  • 这不会引起问题,不,我的意思是,这只是意味着您的表比我预期的要宽,其中大多数是可选的字符串数据类型,@RoyM。
【解决方案2】:

如果您查看生成的查询,您会发现查询不正确。

您需要更改将值获取到游标中的顺序。

FETCH NEXT FROM cursor_setnull INTO
    @curcolumn,
    @curtable;

应该是

FETCH NEXT FROM cursor_setnull INTO
    @curtable,    
    @curcolumn;

【讨论】:

  • 感谢您发现错误。我在玩弄内部函数中没有“声明”的变量@curtable 时错误地引入了这一点。起初我认为缺少一些逻辑(; - 或类似的),所以我将它们转换为仅测试解析器逻辑并忘记了这一点。
猜你喜欢
  • 1970-01-01
  • 2019-08-17
  • 1970-01-01
  • 2012-01-25
  • 1970-01-01
  • 2018-09-07
  • 1970-01-01
  • 2013-03-18
  • 1970-01-01
相关资源
最近更新 更多