一个大的更新查询,或几个单一的更新查询,更新 MySQL 多租户数据库中的值

One big update query, or several single update queries, to update a value in MySQL multi-tenant database

我在 'point of sale' 应用程序 (PHP + MySQL) 中工作,其中的产品、销售额、产品数量必须更新(+ 或 -),具体取决于操作(sale/buy)等

每隔 sale/buy,我将操作数据(日期、客户、总计...)保存在 'invoices' table 中,并将详细信息(发票中的所有产品) 在 'invoicecontents' table.

好的,你知道了。

现在(它工作正常),我执行以下操作来保存发票并更新产品数量:

  1. 保存发票数据
  2. 迭代发票中的产品(来自 JSON)并创建单个 INSERT 查询以在第二个 table.
  3. 中保存发票内容
  4. 之后,保存发票和详细信息后,我会使用单个查询更新产品数量。

类似这样的事情(对于销售操作):

UPDATE products a 
   SET QUANTITY = QUANTITY -
         (SELECT sum(QUANTITY) 
            FROM details b 
           WHERE IDPRODUCT = a.ID   
             and IDCUSTOMER = 4 
             and IDMOV = 615 
             and IDPRODUCT <> -1)
 where ID in 
         (SELECT IDPRODUCT 
            FROM details b 
           WHERE b.IDPRODUCT = a.ID 
             and IDCUSTOMER = 4 
             and IDMOV = 615 
             and IDARTICULO <> -1) 
   and IDCUSTOMER = 4 
   and a.ACTSTOCK = 1;

这很好用。 当然一切都在“begin_transaction...提交”.

但是,我想知道第二种方法是否更好、更快甚至更安全安全:

  1. 保存发票数据
  2. 迭代发票中的产品(来自 JSON)并...
  3. 在迭代中,每个产品(每个 json 项目): 3.1.将该行保存到发票内容中 table 3.2.更新产品 table 中该产品的产品数量。

像这样,每个 product/item:

INSERT INTO detailtable
(IDCUSTOMER,IDPRODUCT,QUANTITY......)
VALUES
(4,615,5);

UPDATE products 
SET QUANTITY = QUANTITY - 5
WHERE 
    IDPRODUCT = 615   
    and IDCUSTOMER = 4
    and ACTSTOCK = 1;

也许这第二种方法更简单,更可以理解,但我真的不知道这是否会导致更多(或更少)CPU,内存消耗,考虑到它是一个多租户 database/application.

我对第一种方法的问题是,在未来,我需要更新更多的字段和 tables 每个销售的产品,所以大更新查询会更大甚至不可能。

谢谢!

当然,在其他条件相同的情况下,单次查询通常比多次查询效率更高。但任何一种方法都应该有效。代码的清晰度和可读性对于长期使用 SQL 的应用程序至关重要。

如果您确实选择了多个查询,请始终以相同的顺序处理您的产品:始终按 ID 顺序对 JSON 对象中的项目列表进行排序,然后再逐一处理它们一。这将有助于避免您的事务出现死锁。

在任何一种情况下,尤其是多查询情况下,请注意为事务中所有查询中的 WHERE 子句创建高效索引。交易时间越短越好。

编辑 为获得最佳性能和最少死锁还需要做一件事。

开始事务后,立即锁定要在事务中更新的 products 中的行。 运行 这个 SELECT 查询 -- 它与您向我们展示的 UPDATE 查询非常相似。您不需要它的结果集,只需要 FOR UPDATE 子句。

SELECT COUNT(ID) FROM (
    SELECT ID 
      FROM products
     WHERE ID in 
             (SELECT IDPRODUCT 
                FROM details b 
               WHERE b.IDPRODUCT = a.ID 
                 and IDCUSTOMER = 4 
                 and IDMOV = 615 
                 and IDARTICULO <> -1) 
       
       and IDCUSTOMER = 4 
       and a.ACTSTOCK = 1
     ORDER BY ID
       FOR UPDATE
) subq;

当您提交(或回滚)事务时,FOR UPDATE 锁被释放。

ORDER BY ID 子句避免了死锁。

都没有。在 `UPDATE.

中使用 JOIN

还有

products: INDEX(IDCUSTOMER, IDPRODUCT, ACTSTOCK)
details:  INDEX(IDCUSTOMER, IDPRODUCT, IDMOV)