【问题标题】:Is there a way to optimize this update query?有没有办法优化这个更新查询?
【发布时间】:2011-03-04 22:04:48
【问题描述】:

我有一个名为“parent”的主表和一个名为“childs”的相关表

现在我对主表运行查询,以使用来自子表的总和来更新一些值,如下所示。

UPDATE master m SET
    quantity1 = (SELECT SUM(quantity1) FROM childs c WHERE c.master_id = m.id),
    quantity2 = (SELECT SUM(quantity2) FROM childs c WHERE c.master_id = m.id),
    count =  (SELECT COUNT(*) FROM childs c WHERE c.master_id = m.id)
WHERE master_id = 666;

这按预期工作,但不是一个好的风格,因为我基本上对同一个结果进行多个 SELECT 查询。有没有办法优化它? (首先进行查询并存储值不是一种选择。

我试过了:

UPDATE master m SET (quantity1, quantity2, count) = (
    SELECT SUM(quantity1), SUM(quantity2), COUNT(*)
       FROM childs c WHERE c.master_id = m.id
) WHERE master_id = 666;

但这不起作用。

更新:这是解决方案,感谢大家:

你可以这样做:

UPDATE master m
  INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = c.quantity1,
    master.count = 1

如果您一次只有一个子记录。但是,如果您想在不工作的联接表中使用像 SUM() 这样的组函数。如果您离开“分组依据”部分,您会得到“组函数的无效使用”,或者如果您使用“GROUP BY c.master_id”,则会出现“您的 sql 语法有错误”

-- This doesnt work :(
UPDATE master m
  INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = SUM(c.quantity1),
    master.count = COUNT(c.*)
GROUP by c.master_id

解决方案是使用带有子查询的 JOIN:

UPDATE master m
  INNER JOIN 
  (
    SELECT   master_id,
             SUM(quantity1) as quantity1,
             COUNT(*) as count
    FROM     childs c 
    GROUP BY master_id
  ) c
  ON c.master_id = m.master_id
SET m.quantity1 = c.quantity1,
    m.count = c.count
WHERE   m.master_id = 666;

但是由于这会从子表中提取每一行,因此开销可能会比使用更多子查询(如在原始 sql 中)更大。因此,您应该在连接表中添加 WHERE 子句,以仅获取您需要的行。

另一种有趣的方法是这种语法,它与使用 WHERE 子句的 JOIN 相同,但仅当您想要更新具有相同值的所有行并且您的子查询仅返回一行时才应使用,因为结果来自子查询被附加到结果中,可以像任何列一样使用。

UPDATE master m,
    (
      SELECT SUM(c.quantity1) as sum_of_quantity,
      COUNT(*) as rowcount FROM child c WHERE c.master_id = 666
    ) as c
SET m.quantity1 = c.sum_of_quantity,
    m.count = c.rowcount
WHERE m.master_id = 666;

【问题讨论】:

  • 您是否尝试过在更新之前进行计数并将其存储在变量中以传递到您的更新中?
  • 你有什么错误?您的更新是正确的。
  • 看起来您有几个答案应该在下面起作用,但只是想指出这似乎是重复列和重复数据之间的非常非规范化的设计。

标签: mysql sql sql-update mysql-error-1111


【解决方案1】:

重写Lieven对MySQL的解决方案:

UPDATE master m 
JOIN (
    SELECT  master_id
            , SUM(quantity1) as quantity1
            , SUM(quantity2) as quantity2 
            , COUNT(*) as count
    FROM    childs c 
    GROUP BY
            master_id
) c
ON c.master_id = m.master_id
SET
  m.quantity1 = c.quantity1
  ,m.quantity2 = c.quantity2
  ,m.count = c.count
WHERE   m.master_id = 666;

【讨论】:

  • 工作就像一个魅力,但(看看我的问题中的更新)不要忘记在子查询中添加 where 子句,否则您将从数据库中提取数百万行只是为了加入一条记录; )
【解决方案2】:

您可以将数据选择到临时表中,然后使用该数据进行更新。

如果您还想在同一往返中插入“新”数据,请查看 INSERT INTO ... SELECT FROM ... ON DUPLICATE KEY UPDATE ...

如果你已经在行不存在的情况下进行插入,那么对于这个例子来说这将是多余的。

示例:

INSERT INTO master m (id, quantity1, quantity2, count)
SELECT master_id, SUM(quantity1) q1, SUM(quantity2) q1, COUNT(*) c
   FROM childs
   GROUP BY master_id
ON DUPLICATE KEY UPDATE 
   m.quantity1 = q1,
   m.quantity2 = q2,
   m.count = c

注意!这是未经测试的代码,但我认为应该可以在 UPDATE 中反向引用选择结果。

语法参考:http://dev.mysql.com/doc/refman/5.0/en/insert.html

【讨论】:

    【解决方案3】:

    我不知道 MySQL 是否允许,但 SQL Server 允许您在更新中使用选择的结果。

    UPDATE master m SET
      quantity1 = c.quantity1
      , quantity2 = c.quantity2
      , count = c.count
    FROM  master m
          INNER JOIN (
            SELECT  master_id
                    , quantity1 = SUM(quantity1)
                    , quantity2 = SUM(quantity2)
                    , count = COUNT(*) 
            FROM    childs c 
            WHERE   master_id = 666
            GROUP BY
                    master_id
          ) c ON c.master_id = m.master_id
    

    【讨论】:

    • 如果您从我的答案中复制/粘贴 MySQL 的正确语法,我将删除该语法。
    • @Wrikken,如果您确定这不适用于 MySQL,无论如何,OP 应该接受您的回答。
    • 好吧,假设这是一个共同的努力(起初尝试直接加入和组是错误的,当然那是行不通的,我在想什么......)
    • +1 因为这是第一个。然而,Wrikken 是对的。对于 MySQL,SET ... 部分位于 JOIN 之后。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    • 1970-01-01
    • 2021-10-27
    相关资源
    最近更新 更多