关于sql server:带连接的SQL表更新

SQL Table Update with join

本问题已经有最佳答案,请猛点这里访问。

我有两张表,表1和表2,它们没有列数。我需要根据表2更新表1。我想更新表1中的所有列,这些列在表2中的单个列中列出。

例如

1
2
3
4
5
6
7
8
9
10
11
    Table1   A B C D E . . .
             1 2 3 4 5 . . .
             7 6 5 4 3 . . .

    Table2   X Y Col_Nam Col_Value
             1 2    C       8
             1 2    D       9
             7 6    E       10
             7 6    C       20
             . .    .       .
             . .    .       .

当匹配以下条件table1.a=table2.x时,更新表1中列出的所有列。表1.b=表2.y

平台是SQL Server。我要寻找的是一个动态的解决方案,因为我不知道要更新的列名。表1可以有n个需要更新的列。我用光标尝试了以下操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DECLARE @s VARCHAR(MAX), @key1 VARCHAR(MAX), @key2 VARCHAR(MAX), @Cname VARCHAR(MAX), @CValue VARCHAR(MAX)

DECLARE crs CURSOR FOR SELECT * FROM Table2
OPEN crs;
FETCH NEXT FROM crs INTO @key1,@key2,@Cname,@Cvalue;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @s =
                'Update T1 SET ' +  @FieldName + ' = ''' + @FieldValue +
        ''' from Table1 T1' +
        ' where T1.A = '''  + @key1 +
        ''' and T1.B = ''' + @key2

EXEC(@s)


    FETCH NEXT FROM crs INTO @key1,@key2,@Cname,@Cvalue;

END

CLOSE crs
DEALLOCATE crs

不知怎么的,它不起作用,我想为所有那些不匹配Where条件的记录设置一个默认值。

如有任何其他解决方案或帮助,我们将不胜感激。


警告:在使用任何动态SQL之前,请阅读有关SQL注入的信息。在您的例子中,如果用户有权访问表2,则可以通过将SQL代码写入col_name来对其进行黑客攻击。

SQL小提琴示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
DECLARE @X INT, @Y INT, @stmt nvarchar(MAX), @params nvarchar(MAX)

SELECT @params = '@X int, @Y int'

DECLARE table_cursor cursor LOCAL fast_forward FOR
    SELECT DISTINCT X, Y FROM Table2

OPEN table_cursor
while 1 = 1
BEGIN
    fetch table_cursor INTO @X, @Y
    IF @@fetch_status <> 0 break

    SELECT @stmt = NULL
    SELECT @stmt =
        isnull(@stmt + ', ', '') +
        Col_Name + ' = ' + CAST(Col_Value AS nvarchar(MAX))
    FROM Table2
    WHERE X = @X AND Y = @Y

    SELECT @stmt = 'update Table1 set ' + @stmt + ' where A = @X and B = @Y'

    EXEC dbo.sp_executesql
        @stmt = @stmt,
        @params = @params,
        @X = @X,
        @Y = @Y
END
close table_cursor
deallocate table_cursor

要做到这一点,标准的SQL方法需要相关的子查询:

1
2
3
4
5
6
7
8
9
10
11
UPDATE table1
    SET C = COALESCE((SELECT MAX(col_value)
                      FROM Table2 t2
                      WHERE table1.A = t2.X AND table1.B = t2.Y AND
                            t2.Col_Name = 'A'
                     ), C),
        D = COALESCE((SELECT MAX(col_value)
                      FROM Table2 t2
                      WHERE table1.A = t2.X AND table1.B = t2.Y AND
                            t2.Col_Name = 'D'
                     ), D)

某些SQL引擎允许联接。下面是一个用于MySQL的方法:

1
2
3
4
5
6
7
8
9
UPDATE table1 JOIN
       (SELECT X, Y, MAX(CASE WHEN col_name = 'C' THEN col_value END) AS C,
               MAX(CASE WHEN col_name = 'D' THEN col_value END) AS D
        FROM table2
        GROUP BY X, Y
       ) t2
       ON t2.X = table1.A AND t2.Y = table2.Y
    SET C = COALESCE(t2.C, C),
        D = COALESCE(t2.D, D)

在这两种情况下,当不匹配时,coalesce()都是为了保持当前值。如果你想要没有匹配的NULL,那么只需移除coalesce()

编辑

在SQL Server中,更新/联接的语法略有不同:

1
2
3
4
5
6
7
8
9
10
UPDATE table1 JOIN
    SET C = COALESCE(t2.C, C),
        D = COALESCE(t2.D, D)
    FROM table1 JOIN
         (SELECT X, Y, MAX(CASE WHEN col_name = 'C' THEN col_value END) AS C,
                 MAX(CASE WHEN col_name = 'D' THEN col_value END) AS D
          FROM table2
          GROUP BY X, Y
         ) t2
         ON t2.X = table1.A AND t2.Y = table2.Y;