【问题标题】:MySQL - Update join & incrementMySQL - 更新加入和增量
【发布时间】:2014-01-04 13:29:42
【问题描述】:

this sqlfiddle(如果小提琴将来死了,也会在下面)。目标是将所有已签出的商品返回到 items 表,更新数量值。

CREATE TABLE checkout(
    id INT NOT NULL
    -- toUser, indexes, etc
);

CREATE TABLE inventory(
    id INT NOT NULL AUTO_INCREMENT,
    quantity INT DEFAULT 0,
    PRIMARY KEY (id)
);

INSERT INTO inventory(quantity) VALUES(90);
INSERT INTO inventory(quantity) VALUES(42);

-- 10 values
INSERT INTO checkout(id) VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1);

-- 8 values
INSERT INTO checkout(id) VALUES(2),(2),(2),(2),(2),(2),(2),(2);

-- Return all the checked out items back to the inventory (up the quantity)
UPDATE inventory i
INNER JOIN checkout c ON c.id = i.id
SET i.quantity = i.quantity + 1;

在这次更新之后,我期待这个结果:

Inventory:
id = 1, quantity = 100
id = 2, quantity = 50

相反,收到的是:

Inventory:
id = 1, quantity = 91
id = 2, quantity = 43

一个连接返回 18 行:

SELECT * FROM inventory i INNER JOIN checkout c ON c.id = i.id;

我认为我对 UPDATE 操作的理解存在漏洞。感谢您的帮助。

【问题讨论】:

  • 所以问题是你为什么期望 100 和 50?JOIN 是 ON ID,库存中只有 2 个不同的 id,所以 90+1 和 42+1。
  • @Mihai 将 SELECT 的 UPDATE 替换为 SELECT * FROM inventory i INNER JOIN checkout c ON c.id = i.id,生成我正在寻找的所有行。然后,我期望沿着行走。根据@eggyal,这种行为本质上是未定义的,就像你提到的那样看起来像 DISTINCT。谢谢-

标签: mysql sql sql-update


【解决方案1】:

UPDATE Syntax 中所述:

对于多表更新,不能保证以任何特定顺序执行分配。

换句话说,由于inventory 表中的每条记录多次连接到checkout 表,因此不能保证赋值右侧的i.quantity 的值将反映先前的更新。

作为@newfurniturey suggests,您可以从相关的COUNT() 子查询中改为UPDATE。但是,我更想在这里定义triggers

CREATE TRIGGER checkout_ins AFTER INSERT ON checkout FOR EACH ROW
  UPDATE inventory SET quantity = quantity + 1 WHERE id = NEW.id;

CREATE TRIGGER checkout_upd AFTER UPDATE ON checkout FOR EACH ROW
  UPDATE inventory SET quantity = quantity + CASE id
    WHEN OLD.id THEN -1
    WHEN NEW.id THEN +1
  END WHERE id IN (OLD.id, NEW.id);

CREATE TRIGGER checkout_del AFTER DELETE ON checkout FOR EACH ROW
  UPDATE inventory SET quantity = quantity - 1 WHERE id = OLD.id;

【讨论】:

  • 感谢您的建议!我可能最终会在这里和其他地方使用它。
  • 编辑:插入时,数量应减少,删除应增加。
  • 可以从结帐中删除不再用于库存(例如被购买)的商品。使用上述解决方案,从结帐中删除该项目会将其放回库存中。触发器是一个很好的例子吗?
  • @nous:我不会尝试在应用程序的数据库层中处理此类业务逻辑:而是从更高层内显式更新inventory
【解决方案2】:

如果我了解您的要求,您想为checkout 中具有相同id 的每条记录“添加”1quantity 列吗?例如,如果 checkout 中有 10 个条目对应于 id = 1,那么您将拥有 quantity = quantity + 10...

如果是这种情况,您需要在UPDATE 语句中使用group by 函数(而不是JOIN);您可以通过子查询和COUNT()

完成此操作
UPDATE inventory i
SET i.quantity = i.quantity + (
    SELECT COUNT(c.id) FROM checkout c WHERE c.id = i.id
);

结果:

SELECT * FROM inventory;

ID  QUANTITY
1   100
2   50

SqlFiddle Demo

【讨论】:

  • 这会产生对库存的全表扫描吗?
  • UPDATE 语句会,是的,因为没有指定 WHERE 子句。如果没有,它将有效地命中表中的每一行。如果你希望它只做一个子集,你需要指定一个WHERE 子句(如果它还定义了适当的索引会更有效,但这是一个不同的主题)
  • WHERE i.id IN (SELECT checkout.id FROM checkout GROUP BY checkout.id)。让我们为我们的讨论假设适当的索引,对此WHERE 的任何其他批评?
  • @nous:只有当库存物品比以往任何时候都多得多时,这才有意义。
  • @nous 如果您已经在UPDATE 本身中执行它,我不会向WHERE 添加额外的SELECT。如果您在checkout.id 上有一个索引,那么该表中的SELECT COUNT(*) 应该是一个非常快速的查找;但是,如果您要在WHERE 中使用SELECT,我建议您使用EXISTS:WHERE EXISTS (SELECT * FROM checkout c WHERE c.id = i.id),因为它应该更快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
  • 2015-04-09
  • 1970-01-01
相关资源
最近更新 更多