【问题标题】:MERGE INTO table containing AUTO_INCREMENT columns包含 AUTO_INCREMENT 列的 MERGE INTO 表
【发布时间】:2011-06-10 12:48:59
【问题描述】:

我已声明下表供审核触发器使用:

CREATE TABLE audit_transaction_ids (id IDENTITY PRIMARY KEY, uuid VARCHAR UNIQUE NOT NULL, `time` TIMESTAMP NOT NULL);
  1. 触发器将在同一个事务中被多次调用。

  2. 第一次调用触发器时,我希望它插入一个新的 具有当前 TRANSACTION_ID() 和时间的行。

  3. 随后调用触发器的时间,我希望它返回 现有的“id”(为此我调用 Statement.getGeneratedKeys() ) 不改变“uuid”或“时间”。

目前的架构似乎有两个问题。

  1. 当我调用 MERGE INTO audit_transaction_ids (uuid, time) KEY(id) VALUES(TRANSACTION_ID(), NOW()) 时,我得到:org.h2.jdbc.JdbcSQLException: Column "ID" contains null values; SQL statement: MERGE INTO audit_transaction_ids (uuid, time) KEY(id) VALUES (TRANSACTION_ID(), NOW()) [90081-155]

  2. 我怀疑在现有行上调用 MERGE 会改变“时间”。

如何解决这两个问题?

【问题讨论】:

    标签: sql h2 sql-merge


    【解决方案1】:

    MERGE 类似于java.util.Map.put(key, value):如果行不存在,它将插入该行,如果存在则更新该行。话虽如此,只要您使用另一列作为键,您仍然可以合并到包含 AUTO_INCREMENT 列的表中。

    给定customer[id identity, email varchar(30), count int] 你可以merge into customer(id, email, count) key(email) values((select max(id) from customer c2 where c2.email='test@acme.com'), 'test@acme.com', 10)。意思是,如果记录存在则重用id,否则使用null。

    另请参阅https://stackoverflow.com/a/18819879/14731,了解根据行是否已存在进行插入或更新的可移植方式。


    1. MERGE INTO audit_transaction_ids (uuid, time) KEY(id) VALUES(TRANSACTION_ID(), NOW())

    如果您只想插入新行,请使用: INSERT INTO audit_transaction_ids (uuid, time) VALUES(TRANSACTION_ID(), NOW())

    MERGE 不设置列ID 的值如果ID 用作键是没有意义的,因为这样它永远不会(即使在理论上)更新现有行。您可以做的是使用另一个键列(在上述情况下,没有可以使用的列)。有关详细信息,请参阅MERGE 的文档。

    2。在现有行上调用 MERGE 将改变“时间”

    我不确定您是否谈论“时间”列的值已更改的事实。如果您使用MERGE ... VALUES(.., NOW()),这是预期的行为,因为MERGE 语句应该更新该列。

    或者你的意思是旧版本的 H2 在同一个事务中返回不同的值(不像大多数其他数据库,在同一个事务中返回相同的值)。确实如此,但是对于 H2 版本 1.3.155 (2011-05-27) 及更高版本,此不兼容性已得到修复。另请参阅change log:“CURRENT_TIMESTAMP() 等现在在事务中返回相同的值。”看起来这不是您的问题,因为您似乎使用的是 1.3.155 版本(错误消息 [90081-155] 包括构建/版本号)。

    【讨论】:

    • @Thomas:暂时忘记了我的特定模式,我认为 MERGE 的目的是在行丢失时插入或如果存在则更新。因此,您应该如何合并到包含 AUTO_INCREMENT 列的表中?我是否误解了 MERGE INTO 的目的?
    • 如果您不知道行是否已存在,MERGE 很有用。通常这意味着您没有自动增量主键。如果您知道给定行不存在,请使用INSERT。如果您知道它存在,请使用UPDATE
    • @Thomas:所以如果我不知道该行是否提前存在,就不可能将 MERGE 与 AUTO_INCREMENT 列一起使用?
    • @Gili 要更新一行,您显然需要知道要更新的哪个行。所以你需要知道表的主键(或另一个唯一键)。但是在您的示例中,唯一键似乎是 id 并且您没有在 values 子句中提供值。那么数据库怎么知道要更新哪一个行呢?
    • @Thomas:我希望查询找到包含特定“uuid”的行。如果不存在这样的行,则插入一个。否则,返回关联的 id。我将如何实现它?
    【解决方案2】:

    简答:

    MERGE INTO AUDIT_TRANSACTION_IDS (uuid, time) KEY (uuid, time) 值(TRANSACTION_ID(),现在());

    性能小提示:确保 uuid 已编入索引

    长答案:

    MERGE 基本上是一个UPDATE,当没有发现要更新的记录时INSERTs。

    维基百科给出了更简洁、标准化的语法 MERGE 但您必须提供自己的更新和插入。 (H2是否支持这不是我的回答)

    那么如何在 H2 中使用 MERGE 更新记录?您定义要查找的键,如果发现更新行(使用您提供的列名,您可以在此处定义DEFAULT,将列重置为其默认值),否则您插入行。

    现在Null 是什么? Null 表示未知、未找到、未定义,任何不是您要查找的内容。

    这就是为什么Null 是要查找的关键。因为这意味着没有找到记录。

    合并到 table1 (id, col1, col2) KEY(id) 值(Null, 1, 2)

    Null 有一个值。它是一个值。

    现在让我们看看你的 SQL。

    合并到 table1 (id, col1, col2) 键(id)值(默认,1、2)

    这意味着什么?对我来说,它说 我有这个 [DEFAULT, 1, 2],id 列中找到我的 DEFAULT, 然后将 col1 更新为 1,将 col2 更新为 2(如果找到)。 否则,插入默认值id,1 到col1,2 到col2

    明白我在那里强调了什么吗?那有什么意思? DEFAULT 是什么?你如何比较DEFAULTid

    DEFAULT 只是一个关键字。

    你可以做类似的事情,

    合并到 table1 (id, col1, timeStampCol) KEY(id) 值 (Null, 1, 默认)

    但不要将 DEFAULT 放在键列中。

    【讨论】:

    • 请添加关于 MERGE INTO 的返回值的讨论。 Statement.getGeneratedKeys() 可以用来检索任何键吗?
    猜你喜欢
    • 2014-05-30
    • 2013-12-06
    • 2019-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 2016-11-30
    相关资源
    最近更新 更多