关闭。这个问题是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/