Cập nhật bảng bằng THAM GIA trong SQL Server?


835

Tôi muốn cập nhật một cột trong bảng tạo liên kết trên bảng khác, ví dụ:

UPDATE table1 a 
INNER JOIN table2 b ON a.commonfield = b.[common field] 
SET a.CalculatedColumn= b.[Calculated Column]
WHERE 
    b.[common field]= a.commonfield
AND a.BatchNO = '110'

Nhưng nó đang phàn nàn:

Msg 170, Cấp 15, Trạng thái 1, Dòng 2
Dòng 2: Cú pháp không chính xác gần 'a'.

Có chuyện gì ở đây vậy?

Câu trả lời:


1598

Bạn hoàn toàn không có UPDATE FROMcú pháp độc quyền của SQL Server . Cũng không chắc chắn lý do tại sao bạn cần tham gia CommonFieldvà cũng lọc trên đó sau đó. Thử cái này:

UPDATE t1
  SET t1.CalculatedColumn = t2.[Calculated Column]
  FROM dbo.Table1 AS t1
  INNER JOIN dbo.Table2 AS t2
  ON t1.CommonField = t2.[Common Field]
  WHERE t1.BatchNo = '110';

Nếu bạn đang làm điều gì đó thực sự ngớ ngẩn - như liên tục cố gắng đặt giá trị của một cột thành tổng hợp của một cột khác (vi phạm nguyên tắc tránh lưu trữ dữ liệu dư thừa), bạn có thể sử dụng CTE (biểu thức bảng chung) - xem tại đâyở đây để biết thêm chi tiết:

;WITH t2 AS
(
  SELECT [key], CalculatedColumn = SUM(some_column)
    FROM dbo.table2
    GROUP BY [key]
)
UPDATE t1
  SET t1.CalculatedColumn = t2.CalculatedColumn
  FROM dbo.table1 AS t1
  INNER JOIN t2
  ON t1.[key] = t2.[key];

Lý do điều này thực sự ngớ ngẩn, là bạn sẽ phải chạy lại toàn bộ bản cập nhật này mỗi khi có bất kỳ hàng nào table2thay đổi. A SUMlà một cái gì đó bạn luôn có thể tính toán trong thời gian chạy và, khi làm như vậy, không bao giờ phải lo lắng rằng kết quả đã cũ.


3
Khi tôi thử điều này, nó không giống như UPDATE table1 a SET a.[field] = b.[field] - loại bỏ bí danh có tác dụng, vì vậyUPDATE table1 a SET [field] = b.[field]
baldmosher

@baldmosher Tôi cá là có một vấn đề khác, bạn có thể đăng một bài repro trên SQL fiddle không?
Aaron Bertrand

1
Không làm việc cho tôi trên MySQL. Tôi đã phải sử dụng như sau (có ý nghĩa hơn) : UPDATE t1 INNER JOIN t2 on t2.col = t1.col SET t1.field=value WHERE t2.col=something.
George

15
@GeorgeRappel tất nhiên, có lẽ sẽ không hoạt động trên nhiều nền tảng khác. Câu hỏi là về SQL Server.
Aaron Bertrand

Giả sử nhiều bản ghi từ t1 đã tham chiếu cùng một bản ghi từ t2 để kết quả nối trong cùng một bản ghi t2 được trả về trong nhiều hàng. Trong ví dụ đầu tiên của bạn, nếu bạn thay vào đó cập nhật t2, nó sẽ cập nhật bản ghi đó nhiều lần hay chỉ một lần?
xr280xr

46

Hãy thử nó như thế này:

begin tran
    UPDATE a 
    SET a.CalculatedColumn= b.[Calculated Column]
    FROM table1 a INNER JOIN table2 b ON a.commonfield = b.[common field] 
    WHERE a.BatchNO = '110'
commit tran

29

Câu trả lời được đưa ra ở trên của Aaron là hoàn hảo:

UPDATE a
  SET a.CalculatedColumn = b.[Calculated Column]
  FROM Table1 AS a
  INNER JOIN Table2 AS b
  ON a.CommonField = b.[Common Field]
  WHERE a.BatchNo = '110';

Chỉ muốn thêm lý do tại sao sự cố này xảy ra trong SQL Server khi chúng tôi cố gắng sử dụng bí danh của một bảng trong khi cập nhật bảng đó, cú pháp đề cập bên dưới sẽ luôn báo lỗi:

update tableName t 
set t.name = 'books new' 
where t.id = 1

trường hợp có thể là bất kỳ nếu bạn đang cập nhật một bảng hoặc cập nhật trong khi sử dụng tham gia.

Mặc dù truy vấn trên sẽ hoạt động tốt trong PL / SQL nhưng không phải trong SQL Server.

Cách chính xác để cập nhật bảng trong khi sử dụng bí danh bảng trong SQL Server là:

update t 
set t.name = 'books new' 
from tableName t 
where t.id = 1

Hy vọng nó sẽ giúp mọi người tại sao lỗi đến đây.


Rất vui, cảm ơn bạn. Câu trả lời của bạn là câu trả lời đúng cho câu hỏi này.
Ola Ström

4
MERGE table1 T
   USING table2 S
      ON T.CommonField = S."Common Field"
         AND T.BatchNo = '110'
WHEN MATCHED THEN
   UPDATE
      SET CalculatedColumn = S."Calculated Column";


2

Tôi thấy hữu ích khi biến CẬP NHẬT thành CHỌN để lấy các hàng tôi muốn cập nhật dưới dạng thử nghiệm trước khi cập nhật. Nếu tôi có thể chọn các hàng chính xác tôi muốn, tôi có thể cập nhật chỉ những hàng tôi muốn cập nhật.

DECLARE @expense_report_id AS INT
SET @expense_report_id = 1027

--UPDATE expense_report_detail_distribution
--SET service_bill_id = 9

SELECT *
FROM expense_report_detail_distribution erdd
INNER JOIN expense_report_detail erd
INNER JOIN expense_report er 
    ON er.expense_report_id = erd.expense_report_id 
    ON erdd.expense_report_detail_id = erd.expense_report_detail_id
WHERE er.expense_report_id = @expense_report_id

2
    UPDATE mytable
         SET myfield = CASE other_field
             WHEN 1 THEN 'value'
             WHEN 2 THEN 'value'
             WHEN 3 THEN 'value'
         END
    From mytable
    Join otherTable on otherTable.id = mytable.id
    Where othertable.somecolumn = '1234'

Nhiều lựa chọn thay thế ở đây .


0

Một cách tiếp cận khác là sử dụng MERGE

  ;WITH cteTable1(CalculatedColumn, CommonField)
  AS
  (
    select CalculatedColumn, CommonField from Table1 Where BatchNo = '110'
  )
  MERGE cteTable1 AS target
    USING (select "Calculated Column", "Common Field" FROM dbo.Table2) AS source ("Calculated Column", "Common Field")
    ON (target.CommonField = source."Common Field")
    WHEN MATCHED THEN 
        UPDATE SET target.CalculatedColumn = source."Calculated Column";

-Merge là một phần của Tiêu chuẩn SQL

-Cũng tôi chắc chắn rằng các cập nhật tham gia bên trong là không xác định .. Câu hỏi tương tự ở đây trong đó câu trả lời nói về điều đó http://ask.sqlservercentral.com/questions/19089/updating-two-tables-USE-single-query. html


3
Trong khi chúng có thể là tiêu chuẩn, tôi sẽ rất cẩn thậnMERGE .
Aaron Bertrand

1
Điều này thật buồn cười vì đúng nghĩa là 5 phút sau khi tôi đăng bài này, tôi đã tình cờ thấy một số cập nhật không xác định có vấn đề trong các sprocs mà tôi đã thừa hưởng :-) nội dung thú vị
Shane Neuville

Điều đó không làm cho hợp nhất tốt hơn, nó chỉ có nghĩa là bạn có các bản cập nhật xấu.
Aaron Bertrand

1
Phải, tôi chỉ là giai thoại :-) Tôi đã có thứ này trong não khi tôi quay trở lại vào vòng quay và đó là điều đầu tiên tôi thấy.
Shane Neuville

2
CTE là Tiêu chuẩn; dấu ngoặc vuông để thoát tên ngớ ngẩn không (dấu ngoặc kép là).
onedaywhen

0

Tôi có cùng một vấn đề .. và bạn không cần thêm một cột vật lý .. vì bây giờ bạn sẽ phải duy trì nó .. những gì bạn có thể làm là thêm một cột chung trong truy vấn chọn:

VÍ DỤ:

select tb1.col1, tb1.col2, tb1.col3 ,
( 
select 'Match' from table2 as tbl2
where tbl1.col1 = tbl2.col1 and tab1.col2 = tbl2.col2
)  
from myTable as tbl1

0

Cách tiếp cận của Aaron ở trên làm việc hoàn hảo cho tôi. Tuyên bố cập nhật của tôi hơi khác một chút vì tôi cần tham gia dựa trên hai trường được nối trong một bảng để khớp với một trường trong bảng khác.

 --update clients table cell field from custom table containing mobile numbers

update clients
set cell = m.Phone
from clients as c
inner join [dbo].[COSStaffMobileNumbers] as m 
on c.Last_Name + c.First_Name = m.Name

-3

Thử:

UPDATE table1
SET CalculatedColumn = ( SELECT [Calculated Column] 
                         FROM table2 
                         WHERE table1.commonfield = [common field])
WHERE  BatchNO = '110'

6
Tôi đang bỏ qua, bởi vì điều này sẽ cập nhật mọi hàng vào table1, không chỉ các hàng có khớp trên trường chung giữa cả hai bảng (thực sự là nối trái và không phải nối trong).
Cᴏʀʏ

@ Cᴏʀʏ: Bạn có nghĩa là nó sẽ cập nhật mọi hàng khớp BatchNo = '110', phải không? Có phải tất cả các downvote là kết quả của hiệu ứng này, hoặc những người khác có lý do khác để bỏ phiếu xuống?
palswim

Tôi yêu cầu bởi vì một số có thể chấp nhận rằng hoạt động CẬP NHẬT có thể đặt một số hàng thành NULLvà biểu mẫu này có thể là một giải pháp cụ thể ít T-SQL hơn.
palswim
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.