SQL Server中UPDATE和DELETE語句結合INNER/LEFT/RIGHT/FULL JOIN的用法


在SQL Server中,UPDATE和DELETE語句是可以結合INNER/LEFT/RIGHT/FULL JOIN來使用的。

 

我們首先在數據庫中新建兩張表:

[T_A]

CREATE TABLE [dbo].[T_A](
    [ID] [int] NOT NULL,
    [Name] [nvarchar](50) NULL,
    [Age] [int] NULL,
 CONSTRAINT [PK_T_A] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

 

[T_B]

CREATE TABLE [dbo].[T_B](
    [ID] [int] NOT NULL,
    [Name] [nvarchar](50) NULL,
    [Age] [int] NULL,
 CONSTRAINT [PK_T_B] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

 

 

UPDATE與INNER/LEFT/RIGHT/FULL JOIN


 

UPDATE結合INNER JOIN:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

UPDATE [T_A]
SET
Age=[T_B].Age
FROM 
[T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

其效果相當於通過下面INNER JOIN查詢,先找出表[T_A]的數據記錄,然后UPDATE這些找出的數據記錄:

SELECT 
[T_A].*,
[T_B].*
FROM
[T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

注意如果表[T_A]中的某行數據與表[T_B]中多行數據匹配上,這種情況下,表[T_A]的該行數據也只會被UPDATE一次,不過用表[T_B]中的哪一行匹配數據去UPDATE表[T_A]是不確定的。

 

UPDATE結合LEFT JOIN:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

UPDATE [T_A]
SET
Age=[T_B].Age
FROM 
[T_A]
LEFT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

其效果相當於通過下面LEFT JOIN查詢,先找出表[T_A]的數據記錄,然后UPDATE這些找出的數據記錄:

SELECT 
[T_A].*,
[T_B].*
FROM
[T_A]
LEFT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

 

UPDATE結合RIGHT JOIN:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300),
(4,N'Mike',400),
(5,N'Bob',500),
(6,N'Clark',600),
(7,N'Sam',700);

UPDATE [T_A]
SET
Age=[T_B].Age
FROM 
[T_A]
RIGHT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

其效果相當於通過下面RIGHT JOIN查詢,先找出表[T_A]的數據記錄,然后UPDATE這些找出的數據記錄:

SELECT 
[T_A].*,
[T_B].*
FROM
[T_A]
RIGHT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

 

UPDATE結合FULL JOIN:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

UPDATE [T_A]
SET
Age=[T_B].Age
FROM 
[T_A]
FULL JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

其效果相當於通過下面FULL JOIN查詢,先找出表[T_A]的數據記錄,然后UPDATE這些找出的數據記錄:

SELECT 
[T_A].*,
[T_B].*
FROM
[T_A]
FULL JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

 

 

DELETE與INNER/LEFT/RIGHT/FULL JOIN


 

DELETE結合INNER JOIN:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

DELETE [T_A]
FROM 
[T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

其效果相當於通過下面INNER JOIN查詢,先找出表[T_A]的數據記錄,然后DELETE這些找出的數據記錄:

SELECT 
[T_A].*
FROM
[T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

 

DELETE結合LEFT JOIN:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

DELETE [T_A]
FROM 
[T_A]
LEFT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

其效果相當於通過下面LEFT JOIN查詢,先找出表[T_A]的數據記錄,然后DELETE這些找出的數據記錄:

SELECT 
[T_A].*
FROM
[T_A]
LEFT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

 

DELETE結合RIGHT JOIN:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300),
(4,N'Mike',400),
(5,N'Bob',500),
(6,N'Clark',600),
(7,N'Sam',700);

DELETE [T_A]
FROM 
[T_A]
RIGHT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

其效果相當於通過下面RIGHT JOIN查詢,先找出表[T_A]的數據記錄,然后DELETE這些找出的數據記錄:

SELECT 
[T_A].*
FROM
[T_A]
RIGHT JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

 

DELETE結合FULL JOIN:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

DELETE [T_A]
FROM 
[T_A]
FULL JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

其效果相當於通過下面FULL JOIN查詢,先找出表[T_A]的數據記錄,然后DELETE這些找出的數據記錄:

SELECT 
[T_A].*
FROM
[T_A]
FULL JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

 

 

JOIN語句使用子查詢


 

其實我們還可以在UPDATE和DELETE語句使用JOIN時,對UPDATE和DELETE的表使用子查詢,但是這種用法我個人不推薦,我們來看一個UPDATE的例子:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

UPDATE [T_A]
SET
Age=[T_B].Age
FROM 
(
    SELECT
    *
    FROM [T_A]
    WHERE
    [T_A].ID<=2
) AS [T_A]
INNER JOIN
[T_B]
ON [T_A].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

表[T_A]的結果如下所示:

可以看到由於我們現在對表[T_A]做了子查詢,用WHERE條件限制了其ID<=2,所以子查詢只會返回表[T_A]的兩條數據,因此最終表[T_A]只有兩條數據得到了更新。

這個結果是符合我們預期的,但是其中有一個很重要的因素,就是UPDATE關鍵字后面的表名要和子查詢的別名一致,我們對上面的UPDATE語句稍作修改,如下所示:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

UPDATE [T_A]
SET
Age=[T_B].Age
FROM 
(
    SELECT
    *
    FROM [T_A]
    WHERE
    [T_A].ID<=2
) AS [T_A_1]
INNER JOIN
[T_B]
ON [T_A_1].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

現在我們將子查詢的名字命名為了[T_A_1],但是我們UPDATE的表是[T_A],上面語句執行后,表[T_A]的結果如下所示:

我們可以看到表[T_A]的所有數據都被莫名其妙地更新了,我們來看看UPDATE語句的執行計划,如下所示:

我們可以看到最關鍵的一個步驟,也就是表[T_A]和JOIN結果集之間的"Nested Loops"這個JOIN有個警告:"No Join Predicate",其含義就是說表[T_A]和JOIN結果集之間的JOIN是沒有ON條件的,相當於CROSS JOIN,所以我們最后才看到表[T_A]的所有數據都被莫名其妙地更新了。這是因為現在UPDATE語句后面的表名[T_A]和子查詢的命名[T_A_1]不一致,所以UPDATE語句現在不知道如何將[T_A_1]和[T_B]之間INNER JOIN后的結果集對應到UPDATE的表[T_A]中,所以就將表[T_A]的所有數據都更新了。

要解決這個問題其實也很簡單,只要將UPDATE語句后面的表名改為子查詢的名字[T_A_1],使得UPDATE語句后面的表名和子查詢的名字一致就行了,如下所示:

TRUNCATE TABLE [T_A];
TRUNCATE TABLE [T_B];

INSERT INTO [T_A]([ID],[Name],[Age])
VALUES
(1,N'Tome',10),
(2,N'Jack',20),
(3,N'Jim',30),
(4,N'Mike',40),
(5,N'Bob',50);


INSERT INTO [T_B]([ID],[Name],[Age])
VALUES
(1,N'Tome',100),
(2,N'Jack',200),
(3,N'Jim',300);

UPDATE [T_A_1]
SET
Age=[T_B].Age
FROM 
(
    SELECT
    *
    FROM [T_A]
    WHERE
    [T_A].ID<=2
) AS [T_A_1]
INNER JOIN
[T_B]
ON [T_A_1].ID=[T_B].ID;

SELECT * FROM [dbo].[T_A];

現在,表[T_A]的結果如下所示:

可以看到這次UPDATE語句正確地只更新了表[T_A]的兩行數據,我們看看UPDATE語句的執行計划:

可以看到這次"Nested Loops"沒有任何警告,正確地將表[T_A]和[T_B]進行了INNER JOIN,所以UPDATE語句只更新了表[T_A]的兩行數據。這說明雖然我們在UPDATE語句后面寫的是子查詢的名字[T_A_1],但是UPDATE語句還是可以根據子查詢[T_A_1]知道要更新的表實際上是[T_A],不得不說這一點SQL Server還是挺智能的。

但是鑒於在UPDATE和DELETE語句中使用JOIN時,再對UPDATE和DELETE的表使用子查詢看起來比較怪,並且如上所示,用得不對會造成結果出錯,所以我個人還是不推薦在UPDATE和DELETE語句中使用JOIN時,再對UPDATE和DELETE的表使用子查詢,況且這種子查詢實際上完全可以用其它方式來替代。

 

 

總結


 

舉了這么多例子,其實我個人覺得UPDATE和DELETE語句與INNER JOIN結合使用才是最有用的,但是不管是什么JOIN,從上面的例子可以看出,其實都相當於是先用SELECT語句做表[T_A]的INNER/LEFT/RIGHT/FULL JOIN查詢,然后UPDATE或DELETE表[T_A]中查詢出的這些數據記錄。

 

 

參考文獻

UPDATE (Transact-SQL)

DELETE (Transact-SQL)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM