- 我們必須至少發表兩個聲明
- 我們必須考慮性能
- 我們必須考慮比賽條件
- 我們必須在[UPDATE; 如果UPDATE_COUNT = 0 THEN INSERT]和[INSERT; 如果例外然后更新]
- 我們必須對每個更新/插入的記錄執行一次這些語句
總而言之,這是錯誤和挫敗感的重要根源。 同時,使用SQL MERGE語句可能是如此簡單!
MERGE的典型情況
在許多其他用例中,在處理多對多關系時,MERGE語句可能會派上用場。 假設我們有以下模式:
CREATE TABLE documents (id NUMBER(7) NOT NULL,CONSTRAINT docu_id PRIMARY KEY (id));CREATE TABLE persons (id NUMBER(7) NOT NULL,CONSTRAINT pers_id PRIMARY KEY (id));CREATE TABLE document_person (docu_id NUMBER(7) NOT NULL,pers_id NUMBER(7) NOT NULL,flag NUMBER(1) NULL,CONSTRAINT docu_pers_pk PRIMARY KEY (docu_id, pers_id),CONSTRAINT docu_pers_fk_docu FOREIGN KEY (docu_id) REFERENCES documents(id),CONSTRAINT docu_pers_fk_pers FOREIGN KEY (pers_id) REFERENCES persons(id));
上表用于模擬哪個人已閱讀(flag = 1)/已刪除(flag = 2)哪個文檔。 為簡單起見,通常將“ document_person”實體與“ documents”外部聯接,以便“ document-person”記錄的存在或不存在可能具有相同的語義:“ flag IS NULL”表示未讀文檔。
現在,當您要將文檔標記為已讀時,必須決定是插入一個新的“ document_person”,還是更新現有的“ document_person”。 與刪除相同。 與將所有文檔標記為已讀或刪除所有文檔相同。
改用MERGE
您可以一口氣做到這一切! 假設您要插入/更新一條記錄,以將一個文檔標記為已讀:
-- The target tableMERGE INTO document_person dst-- The data source. In this case, just a dummy recordUSING (SELECT :docu_id as docu_id, :pers_id as pers_id, :flag as flagFROM DUAL) src-- The merge condition (if true, then update, else insert)ON (dst.docu_id = src.docu_id AND dst.pers_id = src.pers_id)-- The update actionWHEN MATCHED THEN UPDATE SETdst.flag = src.flag-- The insert actionWHEN NOT MATCHED THEN INSERT (dst.docu_id,dst.pers_id,dst.flag)VALUES (src.docu_id,src.pers_id,src.flag)
這看起來很相似,但是比MySQL的INSERT .. ON DUPLICATE KEY UPDATE語句冗長得多,這更加簡潔。
發揮到極致
但是,您可以走得更遠! 如前所述,您可能還希望將給定人員的所有文檔標記為已讀。 合并沒問題。 如果指定:docu_id,則以下語句與上一條相同。 如果將其保留為空,它將僅將所有文檔標記為:flag:
MERGE INTO document_person dst-- The data source is now all "documents" (or just :docu_id) left outer-- joined with the "document_person" mappingUSING (SELECT d.id as docu_id, :pers_id as pers_id, :flag as flagFROM documents dLEFT OUTER JOIN document_person d_p ON d.id = d_p.docu_id AND d_p.pers_id = :pers_id-- If :docu_id is set, select only that documentWHERE (:docu_id IS NOT NULL AND d.id = :docu_id)-- Otherwise, select all documentsOR (:docu_id IS NULL)) src-- If the mapping already exists, update. Else, insertON (dst.docu_id = src.docu_id AND dst.pers_id = src.pers_id)-- The rest stays the sameWHEN MATCHED THEN UPDATE SETdst.flag = src.flagWHEN NOT MATCHED THEN INSERT (dst.docu_id,dst.pers_id,dst.flag)VALUES (src.docu_id,src.pers_id,src.flag)
jOOQ中的MERGE支持
jOOQ也完全支持MERGE。 有關更多詳細信息,請參見手冊(滾動至底部):
http://www.jooq.org/manual/JOOQ/Query/
合并愉快!
參考:我們的JCG合作伙伴 Lukas Eder在JAVA,SQL和JOOQ博客上使用SQL:2003 MERGE語句 編寫了 奧術魔術 。
相關文章 :
- Java中的數據庫架構導航
- ORM問題
- SQL或NOSQL:這是問題嗎?
- 什么是NoSQL?
- 按匯總分組/多維數據集
翻譯自: https://www.javacodegeeks.com/2011/12/arcane-magic-with-sql2003-merge.html