代码之家  ›  专栏  ›  技术社区  ›  Jürgen Steinblock

是否有优化此更新查询的方法?

  •  0
  • Jürgen Steinblock  · 技术社区  · 14 年前

    我有一个名为“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;
    

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

    我试过这个:

    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;
    

    但那不管用。

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

    你可以这样做:

    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”部分,则会得到“无效使用group函数”;如果使用“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子句,以仅获取所需的行。

    另一个有趣的方法是这种语法,它与join with the where子句的作用相同,但是只有当您希望更新所有具有相同值的行并且子查询只返回一行时才应该使用,因为子查询的结果会附加到结果中,并且可以像任何列一样使用。

    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;
    
    3 回复  |  直到 13 年前
        1
  •  2
  •   Wrikken    14 年前

    将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;
    
        2
  •  1
  •   Lieven Keersmaekers    14 年前

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

    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
    
        3
  •  0
  •   jishi    14 年前

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

    如果您还想在同一个往返行程中插入“新”数据,请查看insert-in to…从中选择…重复密钥更新时…

    如果您已经在执行插入操作(如果行不存在),那么在本例中这是多余的。

    例子:

    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
    

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

    语法引用: http://dev.mysql.com/doc/refman/5.0/en/insert.html