ฉันจะทำคำสั่ง UPDATE กับ JOIN ใน SQL Server ได้อย่างไร


1314

ฉันต้องการอัปเดตตารางนี้ในSQL Serverด้วยข้อมูลจากตาราง 'พาเรนต์' ดูด้านล่าง:

ตาราง: การขาย

id (int)
udid (int)
assid (int)

ตาราง: ud

id  (int)
assid  (int)

sale.assidud.assidมีค่าที่ถูกต้องเพื่อการปรับปรุง

คำถามอะไรที่จะทำเช่นนี้? ฉันกำลังคิดjoinแต่ฉันไม่แน่ใจว่ามันเป็นไปได้


3
คุณใช้ RDBMS รุ่นใดอยู่ MySQL, SQL Server, Oracle, PostgreSQL หรืออย่างอื่น?
395 Chris J คริส

ความสัมพันธ์ระหว่างตารางบ้างไหม? เราจะรู้ได้อย่างไรว่าบันทึกจากการขายนั้นสอดคล้องกับบันทึกใดของ ud มันขึ้นอยู่กับรหัสเป็นคีย์หลักในตารางทั้งสอง?
Cătălin Pitiș

คุณจะอัพเดต UD อย่างไร มีเพียง assid และเป็นรหัสของตัวเอง คุณสามารถยกตัวอย่างในแง่ของค่าจริงที่มีอยู่และบันทึกที่คุณต้องการเปลี่ยนหรือเพิ่มเป็นผลมาจากสคริปต์หรือไม่
Bernhard Hofmann

2
ดูเพิ่มเติมดังนั้นคำถาม ... stackoverflow.com/questions/982919/sql-update-query-using-joins
SteveC

2
นามแฝงของผู้ใช้ในแบบสอบถามเช่นstackoverflow.com/questions/982919/sql-update-query-using-joins
Imran Muhammad

คำตอบ:


2383

ไวยากรณ์ขึ้นอยู่กับ SQL DBMS ที่คุณใช้อย่างเคร่งครัด นี่คือวิธีที่จะทำใน ANSI / ISO (aka ควรทำงานกับ SQL DBMS ใด ๆ ), MySQL, SQL Server และ Oracle โปรดทราบว่าโดยทั่วไปวิธี ANSI / ISO ที่แนะนำของฉันจะช้ากว่าอีกสองวิธี แต่ถ้าคุณใช้ SQL DBMS นอกเหนือจาก MySQL, SQL Server หรือ Oracle ก็อาจเป็นวิธีเดียวที่จะไป (เช่น ถ้า SQL DBMS ของคุณไม่รองรับMERGE):

มาตรฐาน ANSI / ISO:

update ud 
     set assid = (
          select sale.assid 
          from sale 
          where sale.udid = ud.id
     )
 where exists (
      select * 
      from sale 
      where sale.udid = ud.id
 );

MySQL:

update ud u
inner join sale s on
    u.id = s.udid
set u.assid = s.assid

เซิร์ฟเวอร์ SQL:

update u
set u.assid = s.assid
from ud u
    inner join sale s on
        u.id = s.udid

PostgreSQL:

update ud
  set assid = s.assid
from sale s 
where ud.id = s.udid;

โปรดทราบว่าตารางเป้าหมายต้องไม่ซ้ำในส่วนFROMคำสั่งสำหรับ Postgres

ออราเคิล:

update
    (select
        u.assid as new_assid,
        s.assid as old_assid
    from ud u
        inner join sale s on
            u.id = s.udid) up
set up.new_assid = up.old_assid

SQLite:

update ud 
     set assid = (
          select sale.assid 
          from sale 
          where sale.udid = ud.id
     )
 where RowID in (
      select RowID 
      from ud 
      where sale.udid = ud.id
 );

3
ฉันคิดว่า MySQL set assid = s.assidควรเป็นset u.assid = s.assidเช่นนั้น
dotancohen

2
ในไวยากรณ์ ANSI จะเกิดอะไรขึ้นถ้า SELECT หลังจาก=ส่งคืนมากกว่าหนึ่งแถว
ทิ้งบัญชี

2
@ ThrowawayAccount3Million มันอาจจะล้มเหลว AFAIK การดำเนินการเช่นนี้คาดว่าจะมีค่าสเกลาร์และจะโยนข้อผิดพลาดหากให้ชุดผลลัพธ์แทน
ฟรานซิสลอร์ด

6
ฉันหวังว่า OP จะเลือกชื่อที่ดีกว่าสำหรับตารางและคอลัมน์ของเขา !! มันไม่สามารถอ่าน / ใช้งานง่ายเช่นนั้น ...
S.Serpooshan

4
Postgre 9.3 ใช้งานได้เฉพาะupdate ud set assid = s.assid
StackUnder


98

postgres

UPDATE table1
SET    COLUMN = value
FROM   table2,
       table3
WHERE  table1.column_id = table2.id
       AND table1.column_id = table3.id
       AND table1.COLUMN = value
       AND table2.COLUMN = value
       AND table3.COLUMN = value 

20
คำตอบจะมีประโยชน์มากขึ้นหากใช้ชื่อตาราง / คอลัมน์ที่ใช้ในคำถาม ทำไมคำตอบของคุณถึงมี 3 ตาราง?
alfonx

50

แนวทาง SQL มาตรฐานจะเป็น

UPDATE ud
SET assid = (SELECT assid FROM sale s WHERE ud.id=s.id)

บน SQL Server คุณสามารถใช้การเข้าร่วม

UPDATE ud
SET assid = s.assid
FROM ud u
JOIN sale s ON u.id=s.id

1
ด้วยรายการแรกคุณไม่สามารถจับคู่ในคอลัมน์ 2+ แต่เข้าร่วมได้ดี
makciook

6
@makciook: เหรอ? คุณสามารถเพิ่มเงื่อนไขเพิ่มเติมในWHEREข้อถ้าคุณต้องการจับคู่ในคอลัมน์เพิ่มเติม
45790 siride คุณ

2
แค่นิดหน่อย ... แต่ฉันคิดว่า OP หมายถึง sale.udid = ud.id และไม่ลดราคา
Skippy VonDrake


26

การสืบค้นแบบง่ายขึ้นโดยใช้การเข้าร่วมหลายตาราง

   UPDATE
        first_table ft
        JOIN second_table st ON st.some_id = ft.some_id
        JOIN third_table tt  ON tt.some_id = st.some_id
        .....
    SET
        ft.some_column = some_value
    WHERE ft.some_column = 123456 AND st.some_column = 123456

Note - first_table, second_table, third_table และ some_column เช่น 123456 คือชื่อตารางตัวอย่างชื่อคอลัมน์และรหัส แทนที่ด้วยชื่อที่ถูกต้อง


16

อีกตัวอย่างหนึ่งที่ว่าทำไม SQL จึงไม่สามารถพกพาได้จริงๆ

สำหรับ MySQL มันจะเป็น:

update ud, sale
set ud.assid = sale.assid
where sale.udid = ud.id;

สำหรับข้อมูลเพิ่มเติมโปรดอ่านการอัปเดตหลายตาราง: http://dev.mysql.com/doc/refman/5.0/en/update.html

UPDATE [LOW_PRIORITY] [IGNORE] table_references
    SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...
    [WHERE where_condition]

2
+1 ที่ "ทำไม SQL ไม่ได้พกพาจริงๆ" ความคิดเห็น! การพกพามีความเปราะบางมากที่เพิ่งประกาศตัวแปรจะทำลายความสามารถในการพกพาของเครื่องมือฐานข้อมูลยอดนิยมจำนวนมาก
Jeff Moden

8

Teradata Aster เสนอวิธีที่น่าสนใจอีกวิธีในการบรรลุเป้าหมาย:

MERGE INTO ud --what trable should be updated
USING sale -- from what table/relation update info should be taken
ON ud.id = sale.udid --join condition
WHEN MATCHED THEN 
    UPDATE SET ud.assid = sale.assid; -- how to update

8

ฉันคิดว่า SQL-Server หนึ่งในโพสต์บนสุดจะใช้งานได้กับ Sybase เนื่องจากเป็นทั้ง T-SQL แต่น่าเสียดายที่ไม่ใช่

สำหรับ Sybase ฉันพบว่าการอัปเดตต้องอยู่ในตารางไม่ใช่ชื่อแทน:

update ud
set u.assid = s.assid
from ud u
    inner join sale s on
        u.id = s.udid

7

ข้อความต่อไปนี้ที่มีคำหลัก FROM ใช้เพื่ออัปเดตหลายแถวด้วยการเข้าร่วม

UPDATE users 
set users.DivisionId=divisions.DivisionId
from divisions join users on divisions.Name=users.Division

7

MySQL

คุณจะได้ประสิทธิภาพที่ดีที่สุดหากคุณลืมว่าประโยคใดและวางเงื่อนไขทั้งหมดไว้ในนิพจน์ ON

ฉันคิดว่าเป็นเพราะแบบสอบถามแรกต้องเข้าร่วมตารางจากนั้นเรียกใช้ส่วนคำสั่งที่ดังนั้นถ้าคุณสามารถลดสิ่งที่จำเป็นในการเข้าร่วมนั้นเป็นวิธีที่รวดเร็วเพื่อให้ได้ผลลัพธ์ / ทำ udpate

ตัวอย่าง

สถานการณ์

คุณมีตารางผู้ใช้ พวกเขาสามารถเข้าสู่ระบบโดยใช้ชื่อผู้ใช้หรืออีเมลหรือ account_number บัญชีเหล่านี้สามารถใช้งานได้ (1) หรือไม่ใช้งาน (0) ตารางนี้มี 50,000 แถว

จากนั้นคุณมีตารางผู้ใช้ที่จะปิดการใช้งานในครั้งเดียวเพราะคุณพบว่าพวกเขาได้ทำสิ่งที่ไม่ดี อย่างไรก็ตามตารางนี้มีหนึ่งคอลัมน์ที่มีชื่อผู้ใช้อีเมลและหมายเลขบัญชีผสมกัน นอกจากนี้ยังมีตัวบ่งชี้ "has_run" ซึ่งจะต้องตั้งค่าเป็น 1 (จริง) เมื่อมีการเรียกใช้

สอบถาม

UPDATE users User
    INNER JOIN
        blacklist_users BlacklistUser
        ON
        (
            User.username = BlacklistUser.account_ref
            OR
            User.email = BlacklistedUser.account_ref
            OR
            User.phone_number = BlacklistUser.account_ref
            AND
            User.is_active = 1
            AND
            BlacklistUser.has_run = 0
        )
    SET
        User.is_active = 0,
        BlacklistUser.has_run = 1;

เหตุผล

หากเราต้องเข้าร่วมในเงื่อนไข OR ก็จำเป็นต้องตรวจสอบแต่ละแถว 4 ครั้งเพื่อดูว่าควรเข้าร่วมหรือไม่และอาจส่งคืนแถวมากขึ้น อย่างไรก็ตามด้วยการให้เงื่อนไขเพิ่มเติมมันสามารถ "ข้าม" แถวจำนวนมากถ้าพวกเขาไม่ตรงตามเงื่อนไขทั้งหมดเมื่อเข้าร่วม

โบนัส

มันอ่านง่ายขึ้น เงื่อนไขทั้งหมดอยู่ในที่เดียวและแถวที่จะอัปเดตอยู่ในที่เดียว


4

และใน MS ACCESS:

UPDATE ud 
INNER JOIN sale ON ud.id = sale.udid
SET ud.assid = sale.assid;

1
ด้วยความระมัดระวังตลาดหลักทรัพย์จะต้องมาทันทีหลังจากคำจำกัดความของชุดระเบียน! ฉันเพิ่งพยายามหาสถานการณ์ที่คล้ายกันในฐานข้อมูล Access ซึ่งจำเป็นต้องใช้ WHERE clause (จะไม่ยอมรับว่าเป็นเงื่อนไข ON ที่ถูกต้อง) WHERE ต้องมาล่าสุดเพื่อหลีกเลี่ยงข้อผิดพลาดทางไวยากรณ์
Dodecaphone



3

ลองอันนี้ฉันคิดว่าสิ่งนี้จะได้ผลสำหรับคุณ

update ud

set ud.assid = sale.assid

from ud 

Inner join sale on ud.id = sale.udid

where sale.udid is not null

2

สำหรับ SQLite ให้ใช้คุณสมบัติ RowID เพื่อทำการอัพเดท:

update Table set column = 'NewValue'
where RowID = 
(select t1.RowID from Table t1
join Table2 t2 on t1.JoinField = t2.JoinField
where t2.SelectValue = 'FooMyBarPlease');

1
คุณช่วยอธิบายหน่อยได้ไหม?
Mohammed Noureldin

1
@ MohammedNoureldin ฉันจะพยายามอธิบาย ปัญหาคือวิธีการอัปเดตตารางด้วยผลลัพธ์จากแบบสอบถามในการเข้าร่วมโดยใช้ตารางเดียวกัน คำสั่ง (เลือกย่อย) ทำหน้าที่เหมือนกับการเข้าร่วมและส่งกลับค่าฟิลด์ระบบ RowID ซึ่งเป็นหมายเลขเฉพาะสำหรับแต่ละแถวในตาราง เนื่องจากตัวเลือกย่อยสามารถส่งคืนแถวจำนวนมาก "โดยที่ RowID =" เลือกแถวที่ถูกต้องเดียวจากผลลัพธ์ย่อยที่เลือกและทำการอัปเดตไปยังคอลัมน์ แจ้งให้เราทราบหากคุณต้องการคำชี้แจงเพิ่มเติมหรือต้องการทราบความแตกต่างของชุดรูปแบบนี้
KeithTheBiped
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.