sql - 为什么 upsert 不是基本的 SQL 操作

标签 sql upsert

关闭。这个问题是opinion-based .它目前不接受答案。












想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题.

7年前关闭。




Improve this question




为什么 SQL 不支持 upsert 用例?我不是在问如何在特定的 db(*) 中做。我想知道的是为什么 upsert 不是像插入和更新这样的基本操作。我的意思是这是一个非常简单的用例,对吗?我想一定有一些基本的数据库优先原则在执行 upsert 时被破坏,或者数据库引擎在面对 upsert 时面临的一些技术挑战。

*.我了解 mysql 语法和 SQL Merge 语法。顺便说一句,即使在使用这种特定于数据库的语法时,您也需要小心原子性和锁定。并且使用合并语法,创建一个伪表感觉不对。

编辑:我正在编辑此内容以澄清我不是在征求意见。所以我认为这个问题不应该被屏蔽。

最佳答案

因为它不容易处理,无论是酸度还是语法。
更新条件不明确。
例如,在下面的查询中用 upsert 替换“insert into”

insert into t_something 
select * from t_whatever
没有外键,没有主键。
你想怎么更新?
where 条件是选择还是更新?
最终,您必须编写条件,然后您也可以执行“更新/插入 if”...
通常,当您问自己 upsert 问题时,您处理的插入/更新是错误的。
您正在考虑对象而不是固定条件。
你想遍历一个对象数组,如果 count(*) on exists 是 0 则插入,否则更新。
这就是面向对象命令式编程的工作方式,但这不是 SQL 的工作方式。

在 SQL 中,您使用 SET 进行操作。
您可以轻松地进行内部连接 ​​- 在 SET 上更新
和一个左连接,其中在同一个 SET 上插入空值。
这就像合并一样舒适,可读性更强,调试更简单。
它可能会更快。
您已经可以通过将更新和插入放入事务来确保它是原子的。
想到 upsert,接下来你想要哪个白痴? “UpSertLeteTrnc”?梅尔德尔?
或者也许 truncsert ?
到目前为止,还有更重要的事情要做。
这就是我在 SQL-Server 上使用 MERGE 执行 Upsert 的方式:
-- How to create the XML 
/*
DECLARE @xml XML 
SET @xml = ( SELECT (SELECT * FROM T_Benutzer FOR XML PATH('row'), ROOT('table'),  ELEMENTS xsinil) AS outerXml )
-- SELECT @xml 
*/


DECLARE @xml xml 
SET @xml = '<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <row>
    <PLK_UID>7CA68E6E-E998-FF92-BE70-126064765EAB</PLK_UID>
    <PLK_Code>A2 Hoch</PLK_Code>
    <PLK_PS_UID>6CF3B5AB-C6C8-4A12-8717-285F95A1084B</PLK_PS_UID>
    <PLK_DAR_UID xsi:nil="true" />
    <PLK_Name_DE>Mit Legende</PLK_Name_DE>
    <PLK_Name_FR>Avec Légende</PLK_Name_FR>
    <PLK_Name_IT>Con Leggenda</PLK_Name_IT>
    <PLK_Name_EN>With Legend</PLK_Name_EN>
    <PLK_IsDefault>0</PLK_IsDefault>
    <PLK_Status>1</PLK_Status>
  </row>
</table>'


DECLARE @handle INT  
DECLARE @PrepareXmlStatus INT  

EXEC @PrepareXmlStatus = sp_xml_preparedocument @handle OUTPUT, @XML


;WITH CTE AS 
(
    SELECT 
         PLK_UID
        ,PLK_Code
        ,PLK_PS_UID
        ,PLK_DAR_UID
        ,PLK_Name_DE
        ,PLK_Name_FR
        ,PLK_Name_IT
        ,PLK_Name_EN
        ,PLK_IsDefault
        ,PLK_Status
    FROM OPENXML(@handle, '/table/row', 2) WITH 
    (
         "PLK_UID" uniqueidentifier 'PLK_UID[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_Code" character varying(10) 'PLK_Code[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_PS_UID" uniqueidentifier 'PLK_PS_UID[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_DAR_UID" uniqueidentifier 'PLK_DAR_UID[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_Name_DE" national character varying(255) 'PLK_Name_DE[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_Name_FR" national character varying(255) 'PLK_Name_FR[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_Name_IT" national character varying(255) 'PLK_Name_IT[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_Name_EN" national character varying(255) 'PLK_Name_EN[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_IsDefault" bit 'PLK_IsDefault[not(@*[local-name()="nil" and . ="true"])]'
        ,"PLK_Status" int 'PLK_Status[not(@*[local-name()="nil" and . ="true"])]'
    ) AS tSource 

    WHERE (1=1) 
    
    -- AND NOT EXISTS 
    -- (
    --  SELECT * FROM T_VWS_Ref_PdfLegendenKategorie 
    --  WHERE T_VWS_Ref_PdfLegendenKategorie.PLK_UID = tSource.PLK_UID 
    --)
)
-- SELECT * FROM CTE     
MERGE INTO T_VWS_Ref_PdfLegendenKategorie AS A 
USING CTE ON CTE.PLK_UID = A.PLK_UID 
WHEN MATCHED 
    THEN UPDATE
        SET  A.PLK_Code = CTE.PLK_Code
            ,A.PLK_PS_UID = CTE.PLK_PS_UID
            ,A.PLK_DAR_UID = CTE.PLK_DAR_UID
            ,A.PLK_Name_DE = CTE.PLK_Name_DE
            ,A.PLK_Name_FR = CTE.PLK_Name_FR
            ,A.PLK_Name_IT = CTE.PLK_Name_IT
            ,A.PLK_Name_EN = CTE.PLK_Name_EN
            ,A.PLK_IsDefault = CTE.PLK_IsDefault
            ,A.PLK_Status = CTE.PLK_Status
WHEN NOT MATCHED BY TARGET THEN 
INSERT 
(
     PLK_UID
    ,PLK_Code
    ,PLK_PS_UID
    ,PLK_DAR_UID
    ,PLK_Name_DE
    ,PLK_Name_FR
    ,PLK_Name_IT
    ,PLK_Name_EN
    ,PLK_IsDefault
    ,PLK_Status
)
VALUES
(
     CTE.PLK_UID
    ,CTE.PLK_Code
    ,CTE.PLK_PS_UID
    ,CTE.PLK_DAR_UID
    ,CTE.PLK_Name_DE
    ,CTE.PLK_Name_FR
    ,CTE.PLK_Name_IT
    ,CTE.PLK_Name_EN
    ,CTE.PLK_IsDefault
    ,CTE.PLK_Status
)
-- WHEN NOT MATCHED BY SOURCE THEN DELETE
;


EXEC sp_xml_removedocument @handle 

关于sql - 为什么 upsert 不是基本的 SQL 操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25139749/

相关文章:

java - 以编程方式插入行(父行和子行)

mysql - MySQL中字符串出现次数排名

mysql - 如何删除 mysql 中的前导斜杠和尾随斜杠

ruby - 如何更新或插入 Sequel 数据集?

sql - 使用选择和覆盖空值插入或更新

mysql - 对多列进行 COUNT

javascript - 使用复合外键时使用 Sequelize 的 "includes"问题

Postgresql INSERT ... ON CONFLICT ... WHERE 不触发

mysql - 如何将 PostgreSQL "merge_db"(aka upsert)函数转换为 MySQL

sql - 在 SQL Server 上插入更新存储过程