使用UPDATE语句将未提交的数据更新到单元格

时间:2009-12-08 13:58:17

标签: sql sql-server performance sql-update isolation-level

我想将存储在名称 - 值对数据中的表转换为SQL Server 2008中的关系形式。

来源表

Strings
ID  Type    String
100 1   John
100 2   Milton
101 1   Johny
101 2   Gaddar

需要目标

Customers
ID  FirstName   LastName
100 John        Milton
101 Johny       Gaddar

我遵循下面给出的策略,

使用字符串表

中的ID值填充Customer表
INSERT INTO CUSTOMERS SELECT DISTINCT ID FROM Strings

你得到以下

Customers
ID  FirstName   LastName
100 NULL        NULL
101 NULL        NULL

使用ID列将其连接到字符串,以使用其余属性更新客户。这样,客户中的每条记录都会有相应的2条匹配记录。

UPDATE Customers
    SET FirstName = (CASE WHEN S.Type=1 THEN S.String ELSE FirstName)
        LastName = (CASE WHEN S.Type=2 THEN S.String ELSE LastName)
FROM Customers
    INNER JOIN Strings ON Customers.ID=Strings.ID

中间状态将是llike,

ID  FirstName   LastName    ID  Type    String
100 John        NULL        100 1   John
100 NULL        Milton      100 2   Milton
101 Johny       NULL        101 1   Johny
101 NULL        Gaddar      101 2   Gaddar

但这不符合预期。因为在SET子句中分配值时,它只设置提交的值而不是未提交的值。无论如何在UPDATE语句中设置未提交的值(在查询的处理时间内)?

PS:我不是在寻找替代解决方案,而是让我的方法工作,告诉SQL Server使用UPDATE的未提交数据。

3 个答案:

答案 0 :(得分:1)

最简单的方法是将更新分成两部分:

UPDATE Customers
SET FirstName = Strings.String
FROM Customers
INNER JOIN Strings ON Customers.ID=Strings.ID AND Strings.Type = 1

然后:

UPDATE Customers
SET LastName = Strings.String
FROM Customers
INNER JOIN Strings ON Customers.ID=Strings.ID AND Strings.Type = 2

有可能在一个查询中执行此操作,例如派生表,但除非这是特定要求,否则我只使用此方法。

答案 1 :(得分:0)

看看这个,它应该避免你有的所有步骤

DECLARE @Table TABLE(
        ID INT,
        Type INT,
        String VARCHAR(50)
)
INSERT INTO @Table (ID,[Type],String) SELECT 100 ,1   ,'John'
INSERT INTO @Table (ID,[Type],String) SELECT 100 ,2   ,'Milton'
INSERT INTO @Table (ID,[Type],String) SELECT 101 ,1   ,'Johny'
INSERT INTO @Table (ID,[Type],String) SELECT 101 ,2   ,'Gaddar'

SELECT  IDs.ID,
        tName.String NAME,
        tSur.String Surname
FROM    (
            SELECT DISTINCT ID
            FROM @Table
        ) IDs LEFT JOIN
        @Table tName ON IDs.ID = tName.ID AND tName.[Type] = 1  LEFT JOIN
        @Table tSur ON IDs.ID = tSur.ID AND tSur.[Type] = 2

好的,我不认为你会找到你正在寻找的解决方案。从UPDATE (Transact-SQL)开始说明

  

将UPDATE与FROM子句一起使用

     

UPDATE语句的结果是   如果声明包含a,则为undefined   未在中指定的FROM子句   只有一个价值的方式   可用于每个列的出现   更新,即如果更新   陈述不确定。对于   例如,在UPDATE语句中   以下脚本,两行都在   表1符合资格   UPDATE语句中的FROM子句;   但它未定义哪一行   Table1用于更新行   表2。

USE AdventureWorks;
GO
IF OBJECT_ID ('dbo.Table1', 'U') IS NOT NULL
    DROP TABLE dbo.Table1;
GO
IF OBJECT_ID ('dbo.Table2', 'U') IS NOT NULL
    DROP TABLE dbo.Table2;
GO
CREATE TABLE dbo.Table1 
    (ColA int NOT NULL, ColB decimal(10,3) NOT NULL);
GO
CREATE TABLE dbo.Table2 
    (ColA int PRIMARY KEY NOT NULL, ColB decimal(10,3) NOT NULL);
GO
INSERT INTO dbo.Table1 VALUES(1, 10.0), (1, 20.0), (1, 0.0);
GO

UPDATE dbo.Table2 
SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
FROM dbo.Table2 
    INNER JOIN dbo.Table1 
    ON (dbo.Table2.ColA = dbo.Table1.ColA);
GO
SELECT ColA, ColB 
FROM dbo.Table2;

答案 2 :(得分:0)

Astander是对的(我接受他的回答)。 由于读取UNCOMMITTED问题而导致更新没有发生,但由于JOIN返回了多行。我已经验证了这一点。 UPDATE仅选择从多个记录生成的第一行以更新原始表。这是MSSQL,Sybase和此类RDMBMS的行为,但Oracle不允许这种更新,并且它会引发错误。我已经为MSSQL验证了这个问题。

MSSQL再次不支持使用UNCOMMITTED数据更新单元格。不知道其他RDBMS的状态。而且我不知道在查询ISOLATION级别管理中是否有任何RDBMS提供。

另一种解决方案是分两步完成,聚合到unpivot然后插入。与上述答案中给出的方法相比,扫描次数较少。

INSERT INTO Customers
SELECT 
    ID
    ,MAX(CASE WHEN Type = 1 THEN String ELSE NULL END) AS FirstName
    ,MAX(CASE WHEN Type = 2 THEN String ELSE NULL END) AS LastName
FROM Strings
GROUP BY ID 

感谢我的朋友Roji Thomas帮我解决这个问题。