ฟิลด์อัพเดต SQL ของตารางหนึ่งจากฟิลด์ของอีกตารางหนึ่ง


124

ฉันมีสองโต๊ะ:

A [ID, column1, column2, column3]
B [ID, column1, column2, column3, column4]

Aจะเป็นส่วนย่อยของเสมอB(หมายถึงคอลัมน์ทั้งหมดAอยู่ในด้วยB)

ฉันต้องการที่จะปรับปรุงการบันทึกที่มีเฉพาะที่IDอยู่ในBที่มีข้อมูลของพวกเขาจากคอลัมน์ทั้งหมดA Aสิ่งนี้IDมีอยู่ทั้งในAและB.

มีUPDATEไวยากรณ์หรือวิธีอื่นใดที่ทำได้โดยไม่ต้องระบุชื่อคอลัมน์เพียงพูดว่า"set all column of A" ?

ฉันใช้ PostgreSQL ดังนั้นจึงยอมรับคำสั่งที่ไม่ได้มาตรฐาน (อย่างไรก็ตามไม่แนะนำ)


ฉันคิดว่านี่คือสิ่งที่คุณต้องการทำdba.stackexchange.com/a/58383
zubair-0

คำตอบ:


235

คุณสามารถใช้คำสั่งFROM ที่ไม่ได้มาตรฐาน

UPDATE b
SET column1 = a.column1,
  column2 = a.column2,
  column3 = a.column3
FROM a
WHERE a.id = b.id
AND b.id = 1

9
คำถามกำลังถามเกี่ยวกับวิธีการทำโดยไม่ระบุชื่อคอลัมน์ทั้งหมด (และฉันก็เช่นกัน)
เบาะแส

2
ฉันเห็นด้วยกับ @cluesque แต่คำตอบนี้เป็นวิธีที่ยอดเยี่ยมในการใช้ค่าในคอลัมน์หนึ่งในตารางเป็นตารางค้นหาสำหรับการแทนที่ค่าในคอลัมน์ในตารางอื่น (ดูSO 21657475 ) ดังนั้น +1 ...
Victoria Stuart

1
ทำไมต้อง b.id = 1?
YasirAzgar

1
@YasirAzgar b.id = 1 คือการ จำกัด ว่าจะอัปเดตแถวใดใน b มิฉะนั้นเราจะอัปเดตทุกแถวในตาราง บางครั้งนั่นอาจเป็นสิ่งที่คุณต้องการ แต่คำถามเดิมคือการอัปเดตแถวเฉพาะใน b
Scott Bailey

นี่คือสิ่งที่ฉันต้องการสำหรับปัญหาเฉพาะของฉัน: การอัปเดตคอลัมน์ของตารางหนึ่งด้วยค่าจากคอลัมน์ชื่อต่างกันของตารางอื่น
muad-dweeb

49

คำถามนี้เก่า แต่ฉันรู้สึกว่ายังไม่ได้รับคำตอบที่ดีที่สุด

มีUPDATEไวยากรณ์ ... โดยไม่ระบุชื่อคอลัมน์หรือไม่?

วิธีแก้ปัญหาทั่วไปด้วยไดนามิก SQL

คุณไม่จำเป็นต้องทราบชื่อคอลัมน์ใด ๆ ยกเว้นคอลัมน์ที่ไม่ซ้ำกันเพื่อเข้าร่วม ( idในตัวอย่าง) ทำงานได้อย่างน่าเชื่อถือสำหรับกรณีมุมที่เป็นไปได้ที่ฉันคิดได้

สิ่งนี้เฉพาะสำหรับ PostgreSQL ฉันกำลังสร้างโค้ดไดนามิกตามinformation_schemaโดยเฉพาะตารางinformation_schema.columnsซึ่งกำหนดไว้ในมาตรฐาน SQL และ RDBMS ที่สำคัญที่สุด (ยกเว้น Oracle) มีอยู่ แต่DOคำสั่งที่มีโค้ดPL / pgSQL ที่เรียกใช้ไดนามิก SQL นั้นไม่ใช่ไวยากรณ์ PostgreSQL ที่เป็นมาตรฐานโดยสิ้นเชิง

DO
$do$
BEGIN

EXECUTE (
SELECT
  'UPDATE b
   SET   (' || string_agg(        quote_ident(column_name), ',') || ')
       = (' || string_agg('a.' || quote_ident(column_name), ',') || ')
   FROM   a
   WHERE  b.id = 123
   AND    a.id = b.id'
FROM   information_schema.columns
WHERE  table_name   = 'a'       -- table name, case sensitive
AND    table_schema = 'public'  -- schema name, case sensitive
AND    column_name <> 'id'      -- all columns except id
);

END
$do$;

สมมติว่ามีคอลัมน์ที่ตรงกันbสำหรับทุกคอลัมน์ในaแต่ไม่ใช่ในทางกลับกัน bสามารถมีคอลัมน์เพิ่มเติม

WHERE b.id = 123 เป็นทางเลือกเพื่ออัปเดตแถวที่เลือก

SQL Fiddle

คำตอบที่เกี่ยวข้องพร้อมคำอธิบายเพิ่มเติม:

โซลูชันบางส่วนด้วย SQL ธรรมดา

พร้อมรายการคอลัมน์ที่แชร์

คุณยังคงต้องทราบรายชื่อคอลัมน์ที่ทั้งสองตารางใช้ร่วมกัน ด้วยทางลัดทางไวยากรณ์สำหรับการอัปเดตหลายคอลัมน์ - สั้นกว่าคำตอบอื่น ๆ ที่แนะนำในทุกกรณี

UPDATE b
SET   (  column1,   column2,   column3)
    = (a.column1, a.column2, a.column3)
FROM   a
WHERE  b.id = 123    -- optional, to update only selected row
AND    a.id = b.id;

SQL Fiddle

ไวยากรณ์นี้ถูกนำมาใช้กับ Postgres 8.2 ในปี 2006 ก่อนที่จะมีการถามคำถาม รายละเอียดในคู่มือ.

ที่เกี่ยวข้อง:

พร้อมรายการคอลัมน์ใน B

หากAมีการกำหนดคอลัมน์ทั้งหมดNOT NULL(แต่ไม่จำเป็นB)
และคุณทราบชื่อคอลัมน์ของB(แต่ไม่จำเป็นA)

UPDATE b
SET   (column1, column2, column3, column4)
    = (COALESCE(ab.column1, b.column1)
     , COALESCE(ab.column2, b.column2)
     , COALESCE(ab.column3, b.column3)
     , COALESCE(ab.column4, b.column4)
      )
FROM (
   SELECT *
   FROM   a
   NATURAL LEFT JOIN  b -- append missing columns
   WHERE  b.id IS NULL  -- only if anything actually changes
   AND    a.id = 123    -- optional, to update only selected row
   ) ab
WHERE b.id = ab.id;

NATURAL LEFT JOINร่วมแถวจากbที่คอลัมน์ทั้งหมดที่มีชื่อเดียวกันเก็บค่าเดียวกัน เราไม่ต้องการการอัปเดตในกรณีนี้ (ไม่มีอะไรเปลี่ยนแปลง) และสามารถกำจัดแถวเหล่านั้นได้ในช่วงต้นของกระบวนการ ( WHERE b.id IS NULL)
เรายังคงต้องหาแถวที่ตรงกันดังนั้นb.id = ab.idในแบบสอบถามด้านนอก

db <> fiddle ที่นี่
Old sqlfiddle

นี่คือมาตรฐาน SQL ยกเว้นFROMข้อ
มันทำงานเรื่องของคอลัมน์เป็นจริงในปัจจุบันไม่มีAแต่แบบสอบถามไม่สามารถแยกแยะความแตกต่างระหว่างค่าเป็นศูนย์ที่เกิดขึ้นจริงและคอลัมน์ที่ขาดหายไปในAดังนั้นจึงเป็นที่เชื่อถือได้เฉพาะในกรณีที่คอลัมน์ทั้งหมดในจะมีการกำหนดANOT NULL

มีหลายรูปแบบที่เป็นไปได้ขึ้นอยู่กับสิ่งที่คุณรู้เกี่ยวกับทั้งสองตาราง


พลังของ SQL! เพิ่งสังเกตเห็นเมื่อคุณเพิ่มวงเล็บใน set clause ( SET (column1) = (a.column)) Postgres จะถือว่าเป็นการอัปเดตประเภทอื่นและให้และข้อผิดพลาดเช่นนี้:source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
Edgar Ortega

26

ฉันทำงานกับฐานข้อมูล IBM DB2 มานานกว่าทศวรรษและตอนนี้พยายามเรียนรู้ PostgreSQL

ทำงานบน PostgreSQL 9.3.4 แต่ไม่ทำงานบน DB2 10.5:

UPDATE B SET
     COLUMN1 = A.COLUMN1,
     COLUMN2 = A.COLUMN2,
     COLUMN3 = A.COLUMN3
FROM A
WHERE A.ID = B.ID

หมายเหตุ: ปัญหาหลักเกิดจากสาเหตุที่ไม่ได้รับการสนับสนุนใน DB2 และไม่ได้อยู่ใน ANSI SQL

ทำงานบน DB2 10.5 แต่ไม่ทำงานบน PostgreSQL 9.3.4:

UPDATE B SET
    (COLUMN1, COLUMN2, COLUMN3) =
               (SELECT COLUMN1, COLUMN2, COLUMN3 FROM A WHERE ID = B.ID)

ในที่สุด! มันทำงานได้ทั้ง PostgreSQL 9.3.4 และ DB2 10.5:

UPDATE B SET
     COLUMN1 = (SELECT COLUMN1 FROM A WHERE ID = B.ID),
     COLUMN2 = (SELECT COLUMN2 FROM A WHERE ID = B.ID),
     COLUMN3 = (SELECT COLUMN3 FROM A WHERE ID = B.ID)

3
โปรดทราบว่าแบบสอบถามที่สองและสามไม่เทียบเท่ากับคำค้นหาแรกอย่างสมบูรณ์ หากไม่พบแถวที่ตรงกันBคำสั่งแรกจะไม่ทำอะไรเลย (แถวเดิมจะไม่ถูกแตะต้อง) ในขณะที่อีกสองคอลัมน์จะเขียนทับคอลัมน์ด้วยค่า NULL
Erwin Brandstetter

7

นี่เป็นตัวช่วยที่ดี รหัส

UPDATE tbl_b b
SET   (  column1,   column2,   column3)
    = (a.column1, a.column2, a.column3)
FROM   tbl_a a
WHERE  b.id = 1
AND    a.id = b.id;

ทำงานได้อย่างสมบูรณ์

สังเกตว่าคุณต้องมีวงเล็บ "" ใน

From "tbl_a" a

เพื่อให้มันใช้งานได้


5

ไม่จำเป็นต้องเป็นสิ่งที่คุณถาม แต่การใช้มรดก postgres อาจช่วยได้?

CREATE TABLE A (
    ID            int,
    column1       text,
    column2       text,
    column3       text
);

CREATE TABLE B (
    column4       text
) INHERITS (A);

สิ่งนี้หลีกเลี่ยงความจำเป็นในการอัปเดต B

แต่อย่าลืมอ่านรายละเอียดทั้งหมด

มิฉะนั้นสิ่งที่คุณขอจะไม่ถือเป็นแนวทางปฏิบัติที่ดี - สิ่งที่มีพลวัตเช่นมุมมองที่มีSELECT * ...จะถูกกีดกัน (เนื่องจากความสะดวกเพียงเล็กน้อยเช่นนี้อาจทำลายสิ่งต่างๆได้มากกว่าสิ่งที่ช่วยได้) และสิ่งที่คุณขอจะเทียบเท่ากับUPDATE ... SETคำสั่ง


ฉันไม่แน่ใจว่ามรดกจะแก้ปัญหานี้ได้อย่างไร คุณหมายถึงการเพิ่มทริกเกอร์การอัปเดตสำหรับ A ที่อัปเดต B ด้วยหรือไม่? ฉันไม่ต้องการซิงโครไนซ์ A กับ B ตลอดเวลาเมื่อมีการร้องขอเท่านั้น และในกรณีนี้ฉันไม่สามารถใช้ทริกเกอร์ได้
Nir

2
ใช่หากเป็นเพียงในบางกรณีการสืบทอดจะไม่ทำงานและในกรณีนั้นฉันแนะนำให้ใช้วิธีการสืบค้นแบบไดนามิก (ยังคงมีหลายวิธีในการบรรลุสิ่งนี้โดยใช้ภาษาขั้นตอน postgres และหากคุณต้องการใช้ทริกเกอร์คุณก็สามารถใช้ได้เช่นกัน - โดยการเพิ่มฟิลด์ซิงค์เช่นทริกเกอร์การเริ่มทำงานเมื่อตั้งค่าไว้เท่านั้น)
Unreason

0

คุณสามารถสร้างและเรียกใช้ไดนามิก sql เพื่อทำสิ่งนี้ แต่มันไม่เหมาะจริงๆ


ฉันคิดเกี่ยวกับเรื่องนั้น ฉันคิดว่าฉันสามารถทำให้แบบสอบถามของฉันสอดคล้องกับการเปลี่ยนแปลงในภายหลังของทั้งสองตาราง แต่ไดนามิก sql ดูเหมือนจะซับซ้อนเกินไปกว่าที่จะระบุช่องทั้งหมดและลืมเกี่ยวกับความเข้ากันได้ไปข้างหน้า
Nir

ใช่มันจะซับซ้อน แต่ควรส่งต่อให้เข้ากันได้กับคอลัมน์ในภายหลังที่ถูกเพิ่มหรือลบออก ก่อนอื่นคุณจะต้องทำแบบสอบถามเพื่อให้ได้ชื่อคอลัมน์จากทั้งสองตารางจากนั้นจับคู่ชื่อคอลัมน์จากนั้นเขียน dynamic sql เพื่อทำการอัปเดตตามชื่อคอลัมน์ที่ตรงกัน โครงการสนุกจริง :)
Daniel Brink

-4

ลองติดตาม

Update A a, B b, SET a.column1=b.column1 where b.id=1

แก้ไข: - อัปเดตมากกว่าหนึ่งคอลัมน์

Update A a, B b, SET a.column1=b.column1, a.column2=b.column2 where b.id=1

ฉันไม่เข้าใจว่ามันคัดลอก column1, column2 และ column3 อย่างไร และฉันจำเป็นต้องพูดถึง column1 อย่างชัดเจน
Nir

ไม่ได้ผลสำหรับฉัน ฉันได้รับข้อผิดพลาดดังต่อไปนี้ ERROR: ข้อผิดพลาดทางไวยากรณ์ที่หรือใกล้ ","
melbic

1
ไวยากรณ์ที่ไม่ได้มาตรฐานนี้ใช้ได้กับUPDATEใน MySQLแต่ไม่ถูกต้องสำหรับ PostgreSQL
Erwin Brandstetter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.