【问题标题】:How can I update multiple columns with a Replace in SQL server?如何在 SQL Server 中使用 Replace 更新多个列?
【发布时间】:2010-11-13 05:38:26
【问题描述】:

如何更新表中的不同列和行?我想做和replace a string in SQL server类似的事情

我想这样做,但该值存在于同一类型的多个列中。这些值是员工表的外键 varchars。每列代表一个任务,因此同一员工可能被分配到记录中的多个任务,并且这些任务在记录之间会有所不同。我怎样才能有效地做到这一点?基本上可以替换整个表中不同列的所有内容。

感谢您的任何帮助或建议。

干杯, ~ck 在圣地亚哥

【问题讨论】:

  • 这表明您的数据库没有规范化? (即在 3NF 中)
  • 我更像是一个 c# 开发人员,所以我不知道数据库的内部工作原理。我很好奇你将如何正确地表达这种关系?如果表中的每条记录代表一个项目,每列代表一个任务,则可以将一个员工分配给一个或多个任务。副本在哪里?我该如何补救?请指教。
  • 我认为你应该举例说明你拥有什么以及你想要改变什么。如果不了解数据的外观,就很难制定 SQL 查询。
  • 通常你的外键是 int 而不是 varchars。这样你就没有这种类型的问题。这样,单个员工记录可以链接到多个任务,您只需使用员工的 ID。您还可以有一个将员工与任务联系起来的交叉引用表。这意味着 3 个表,而不仅仅是 2 个,但这是很常见的事情。
  • tEmpNum jEmpNum yEmpNum xEmpNum 0 15059 0 15059 13456 13456 13456 13456 15059 15059 15059 15059 15059 15059 15059 15059 15059 15059 15059 15059 - 说我想的15059每次发生更新为13673?尽管这些是数字,但许多值包含诸如 RH6754 之类的文本等等。谢谢。 :)

标签: sql sql-server sql-update dynamic-sql


【解决方案1】:

我知道它已经 12 年了,但我最近有一个类似的要求,这就是我解决它的方法(在没有运气在任何地方找到合适的完整解决方案之后)。不同之处在于我必须更改所有包含某个字符串值的数据库表中的值。下面的过程比较通用,也可以用于一张表。

CREATE OR ALTER PROCEDURE UPDATE_ALL_COLUMNS
    @TableNameSearchFilter NVARCHAR(100),
    @TableSchema NVARCHAR(100),
    @TestValue NVARCHAR(100),
    @NewValue NVARCHAR(100)
AS
BEGIN
    
    DECLARE @NRCOLUMNS INT;
    DECLARE @i INT = 0;
    DECLARE @COLUMN NVARCHAR(100) = '';
    DECLARE @SQL NVARCHAR(MAX) = '';
    DECLARE @TableToUpdate NVARCHAR(256) = '';
    DECLARE @insertingNULL BIT;

    IF (@NewValue IS NULL) SET @insertingNULL = 1
    ELSE SET @insertingNULL = 0;

    WHILE @TableToUpdate IS NOT NULL
    BEGIN
        SELECT @TableToUpdate = MIN(TABLE_NAME)
        FROM INFORMATION_SCHEMA.TABLES
        WHERE TABLE_NAME LIKE @TableNameSearchFilter
            AND TABLE_SCHEMA = @TableSchema
            AND TABLE_NAME > @TableToUpdate;
            
        WITH CTE1 AS
        (
            SELECT ROW_NUMBER() OVER (ORDER BY ORDINAL_POSITION) AS RN
            FROM INFORMATION_SCHEMA.COLUMNS
            WHERE TABLE_NAME = @TableToUpdate
                AND TABLE_SCHEMA = @TableSchema                     
                AND (@insertingNULL = 0 OR (@insertingNULL = 1 AND IS_NULLABLE = 'YES'))
        )
        SELECT @i = MIN(RN), @NRCOLUMNS = MAX(RN) FROM CTE1;

        WHILE (@i <= @NRCOLUMNS AND @TableToUpdate IS NOT NULL)
        BEGIN
            WITH CTE AS
            (
                SELECT *, ROW_NUMBER() OVER (ORDER BY ORDINAL_POSITION) AS RN
                FROM INFORMATION_SCHEMA.COLUMNS
                WHERE TABLE_NAME = @TableToUpdate
                    AND TABLE_SCHEMA = @TableSchema                     
                    AND (@insertingNULL = 0 OR (@insertingNULL = 1 AND IS_NULLABLE = 'YES'))
            )
            SELECT @COLUMN = COLUMN_NAME 
            FROM CTE
            WHERE RN = @i;

            SET @SQL = @SQL + 
                N'UPDATE D SET ' + @COLUMN + N' = ' + ISNULL(N'''' + @NewValue + N'''', N'NULL')
                + N' FROM ' + @TableSchema + N'.' + @TableToUpdate + N' D WHERE CAST(D.' + @COLUMN + ' AS NVARCHAR) = ' + ISNULL(N'''' + @TestValue + N'''', N'NULL') + ';'
                + NCHAR(13) + NCHAR(10);

            SET @i = @i + 1;
        END;        
    END;    

    --PRINT SUBSTRING(@SQL, 1, 4000)
    --PRINT SUBSTRING(@SQL, 4001, 8000)
    --PRINT SUBSTRING(@SQL, 8001, 12000)
    --PRINT SUBSTRING(@SQL, 12001, 16000)
    --PRINT SUBSTRING(@SQL, 16001, 20000)
    --PRINT SUBSTRING(@SQL, 20001, 24000)
    EXEC (@SQL)
END
GO

作为一个使用示例:

EXEC UPDATE_ALL_COLUMNS '%temp%', 'dbo', '', NULL

参数:

  • @TableNameSearchFilter - 这将与LIKE 运算符一起使用,以查找数据库中名称与此值匹配的所有表;
  • @TableSchema - 表的架构(通常是 dbo)
  • @TestValue - 在每个找到的表的所有列(和行)中搜索的值;
  • @NewValue - 替换 @TestValue 的值。 也可以为NULL

解释:

  • EXEC 语句将在数据库的“dbo”模式中查找名称中包含单词“temp”的所有表,然后在找到的所有表的所有列中搜索值“”(空字符串),然后将此值替换为 NULL
  • 显然,如果您的列/表名称或更新值较长,请务必更新参数限制。
  • 确保首先注释最后一行 (EXEC (@SQL)),然后使用 PRINT 取消注释,以便了解该过程的作用以及最终语句的外观。
  • 如果您想搜索NULL 值(即,将@TestValue 设为NULL),这不会起作用(很可能)。尽管如此,也可以很容易地更改它来完成此操作,方法是将WHERE 子句(在动态查询中)中的等号替换为IS NULL,并在@TestValue IS NULL 时删除该行的其余部分。
  • 该过程考虑插入 NULL 值,并且只会在 NULLABLE 列中这样做。

希望这最终对某人有所帮助。

【讨论】:

    【解决方案2】:

    主要思想是创建一个SQL Update语句,不管表有多少字段。它是在 SQL Server 2012 上创建的,但我认为它也适用于 2008 年。

    示例表:

    CREATE TABLE SampleTable
    (
        Field1 INT,
        Field2 VARCHAR(20),
        Field3 VARCHAR(20),
        Field4 VARCHAR(100),
        Field5 DATETIME,
        Field6 NVARCHAR(10)
    );
    

    仅获取 varchar 和 nvarchar 字段。根据您的要求更改 OLD_TEXTNEW_TEXT。如果您不仅需要匹配 varchar 和 nvarchar 字段,请更改 system_type_id 值。

    SELECT 'UPDATE dbo.SampleTable SET ' + STUFF((SELECT ', [' + name + '] =   REPLACE([' + name + '], ''OLD_TEXT'', ''NEW_TEXT'')' 
    FROM sys.COLUMNS
    WHERE 
        [OBJECT_ID] = OBJECT_ID('SampleTable')
        AND [is_identity] = 0 --It's not identity field
        AND [system_type_id] in (167, 231) -- varchar, nvarchar
    FOR XML PATH('')), 1,1, '')
    

    最后一次查询的结果是:

    UPDATE dbo.SampleTable SET  
        [Field2] = REPLACE([Field2], 'OLD_TEXT', 'NEW_TEXT'), 
        [Field3] = REPLACE([Field3], 'OLD_TEXT', 'NEW_TEXT'), 
        [Field4] = REPLACE([Field4], 'OLD_TEXT', 'NEW_TEXT'), 
        [Field6] = REPLACE([Field6], 'OLD_TEXT', 'NEW_TEXT');
    

    只需复制结果并在 SSMS 中执行。这个sn-p可以为你写更新语句节省一点时间。

    希望对你有帮助。

    【讨论】:

    • 很棒的解决方案!
    【解决方案3】:

    回答发布者关于如何规范化此数据结构的补充问题。以下是你的做法:

    Project
    -------
    ProjectID
    ProjectName
    etc...
    
    Employee
    --------
    EmployeeID
    EmployeeName
    etc...
    
    Task
    ----
    TaskID
    ProjectID
    EmployeeID
    TaskDescription
    etc...
    

    您当前的结构,在 Project 表中有一堆 Task1、Task2 等...列,显然不是由了解关系数据库的人设计的。

    在解雇该人的过程中,您可能会解释他的设计违反了First Normal Form,同时将他引导至该链接文章的“跨列重复组”部分。

    【讨论】:

    • 啊,那么所有的赌注都没有了 :) 无论如何,这比“有点非规范化”要大得多。违反的是第一范式,而不是第三范式。有些情况需要以复制数据的方式展平表,但这不是这里发生的情况。这只是无益的痛苦。
    【解决方案4】:

    这应该可以解决问题:

    UPDATE table1
    SET field1 = replace(field1, 'oldstring', 'newstring'),
        field2 = replace(field2, 'oldstring2', 'newstring2')
    

    等等……

    【讨论】:

    • 如果列中不存在字符串,它可以吗?好的,这听起来很酷。我只会将它应用于所有可能的列???谢谢你的提示。我会试试这个。甜!
    • 请不要在生产系统上直接尝试(或至少先做备份)。
    • 另外,您可以使用 where 子句来缩小范围(可能)。
    • 只是为了更新任何感兴趣的人;我遇到了这个问题。数据类型是 varchar(7),如果 'newstring' 的长度与 'oldstring' 相同或更短,一切都很好,但是当我试图转向另一个方向时,数据将被截断,错误抬起了他们丑陋的头。想到了一种不太有效的方法,那就是对存储过程中的每一列运行更新,例如 Begin Update table1 SET field1='newstring' WHERE field1='oldstring' END,然后对 field2、field3、filedn 执行相同的查询.我确信它不是有效的,但结果就是我所寻求的。谢谢大家!
    猜你喜欢
    • 2021-04-24
    • 2023-01-31
    • 1970-01-01
    • 2014-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-27
    • 2011-06-03
    相关资源
    最近更新 更多