MySQL更新连接查询以解决重复值

Posted

技术标签:

【中文标题】MySQL更新连接查询以解决重复值【英文标题】:MySQL update join query to solve duplicate Values 【发布时间】:2015-11-24 14:43:27 【问题描述】:

我有一个类别表,其中包含一些重复的类别,如下所述,

`Categories`
+========+============+============+
| cat_id | cat_name   | item_count |
+========+============+============+
|      1 | Category 1 |         2  |
|      2 | Category 1 |         1  |
|      3 | Category 2 |         2  |
|      4 | Category 3 |         1  |
|      5 | Category 3 |         1  |
+--------+------------+------------+

这是另一个与另一个 Items 表相关的联结表。第一个表中的item_count 是每个cat_id 的项目总数。

`Junction`
+========+=========+
| cat_id | item_id |
+========+=========+
|      1 |     100 |
|      1 |     101 |
|      2 |     102 |
|      3 |     103 |
|      3 |     104 |
|      4 |     105 |
|      5 |     106 |
+--------+---------+

如何将重复类别中的这些项目添加或组合成在其重复项中每个具有最大 item_count 的项目? (例如Category 1)。

此外,如果item_count 与那些重复的相同,则将选择具有最大cat_id 的类别并将item_count 组合到该记录中。 (例如Category 3)。

注意:item_count 不会删除重复记录,而是 设置为0

下面是预期的结果。

+========+============+============+
| cat_id | cat_name   | item_count |
+========+============+============+
|      1 | Category 1 |         3  |
|      2 | Category 1 |         0  |
|      3 | Category 2 |         2  |
|      4 | Category 3 |         0  |
|      5 | Category 3 |         2  |
+--------+------------+------------+

+========+=========+
| cat_id | item_id |
+========+=========+
|      1 |     100 |
|      1 |     101 |
|      1 |     102 |
|      3 |     103 |
|      3 |     104 |
|      5 |     105 |
|      5 |     106 |
+--------+---------+

结果中有两个重复的Category 1Category 3。我们有 2 个场景,

    cat_id=2 被消除,因为它的item_count=1 小于 cat_id=1item_count=2cat_id=4 被消除,即使它的 item_count 相同 与cat_id=5 一样,因为5 是重复项中的最大值 Category 3

如果有任何查询可以连接和更新两个表以解决重复问题,请帮助我。

【问题讨论】:

您想要最小还是最大 cat_id?你写的和表格里的不一样:id=3 cat_id=1 @Strawberry id 列与结果集无关,而是用于索引的自动增量主列 @genespos cat_id = 1 在其重复项中具有最大的item_count,因此与重复项cat_id = 2 相关的项目应添加到cat_id = 1。 如果绑定了 item_counts 怎么办? @Strawberry 在结果中,我们有两种情况,(1)cat_id = 2 被淘汰,因为它的item_count = 1 小于cat_id = 1 的@987654359 @ = 2. (2) cat_id = 4 被淘汰,即使它的item_countcat_id = 5 相同,因为5 是重复的Category 3 中的最大值。 【参考方案1】:

这是一个选择。你可以想办法让它适应 UPDATE ;-)

为了简单起见,我忽略了连接表

SELECT z.cat_id
     , z.cat_name
     , (z.cat_id = x.cat_id) * new_count item_count
  FROM categories x 
  LEFT 
  JOIN categories y 
    ON y.cat_name = x.cat_name 
   AND (y.item_count > x.item_count OR (y.item_count = x.item_count AND y.cat_id > x.cat_id))
  LEFT
  JOIN 
     ( SELECT a.cat_id, b.*
         FROM categories a
         JOIN 
            ( SELECT cat_name, SUM(item_count) new_count, MAX(item_count) max_count FROM categories GROUP BY cat_name) b
           ON b.cat_name = a.cat_name
     ) z
    ON z.cat_name = x.cat_name
 WHERE y.cat_id IS NULL;

+--------+------------+------------+
| cat_id | cat_name   | item_count |
+--------+------------+------------+
|      1 | Category 1 |          3 |
|      2 | Category 1 |          0 |
|      3 | Category 2 |          2 |
|      4 | Category 3 |          0 |
|      5 | Category 3 |          2 |
+--------+------------+------------+

【讨论】:

+1 再次感谢,您的查询可以解决第一个表。现在我正在尝试第二个的更新查询部分。 :) 最简单的方法:UPDATE categories x JOIN (the query) y ON y.cat_id = x.cat_id SET x.item_count = y.item_count; 但请注意,存储派生数据通常不是一个好主意。【参考方案2】:
 DELIMITER $$
 DROP PROCEDURE IF EXISTS  cursor_proc $$
 CREATE PROCEDURE cursor_proc()
 BEGIN
   DECLARE @cat_id   INT;
   DECLARE @cat_name VARCHAR(255);
   DECLARE @item_count INT;

   DECLARE @prev_cat_Name VARCHAR(255);
   DECLARE @maxItemPerCategory INT;
   DECLARE @maxItemId INT DEFAULT 0;
   DECLARE @totalItemsCount INT;
   -- this flag will be set to true when cursor reaches end of table
   DECLARE exit_loop BOOLEAN;         
   -- Declare the cursor
   DECLARE categories_cursor CURSOR FOR
     SELECT select cat_id ,cat_name ,item_count from Categories Order By cat_name, cat_id;
   -- set exit_loop flag to true if there are no more rows
   DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
   -- open the cursor
   OPEN categories_cursor;
   -- start looping
   categories_loop: LOOP
     -- read the name from next row into the variables 
     FETCH  categories_cursor INTO @cat_id, @cat_name, @item_count ;

     -- close the cursor and exit the loop if it has.
     IF exit_loop THEN
         CLOSE categories_loop;
         LEAVE categories_loop;
     END IF;

       IF(@prev_cat_Name <> @cat_name)
        THEN 
        -- Category has changed, set the item_count of the 'best' category with the total items count
        IF(@maxItemId > 0)
        THEN
          UPDATE Categories  
            SET Categories.item_count=@totalItemsCount
           WHERE Categories.cat_id=@maxItemId;         
        END IF;

       -- Reset Values with the actual row values                        
          SET @maxItemPerCategory = @item_count;
          SET @prev_cat_Name = @cat_name;
          SET @maxItemId = @cat_id
          SET @totalItemsCount = @item_count;
       ELSE
      -- increment the total items count
          SET @totalItemsCount = @totalItemsCount + @item_count

       -- if the actual row has the maximun item counts, then it is the 'best'
           IF (@maxIntPerCategory < @item_count)
           THEN 
             SET @maxIntPerCategory = @item_count
             SET @maxItemId = @cat_id
           ELSE
         -- else, this row is not the best of its Category
              UPDATE Categories  
                 SET Categories.item_count=0
               WHERE Categories.cat_id=@cat_id; 
           END IF; 

       END IF;


   END LOOP categories_loop;
 END $$
 DELIMITER ;

【讨论】:

@ alessalessio @Mikey 你能详细说明一下吗? :) 我的短一点;) “更短”并不总是“更好”;)【参考方案3】:

它不漂亮,部分是从草莓的 SELECT 中复制的

UPDATE categories cat, 
    junction jun,
    (select 
    (z.cat_id = x.cat_id) * new_count c,
     x.cat_id newcatid,
     z.cat_id oldcatid
    from categories x 
      LEFT 
      JOIN categories y 
        ON y.cat_name = x.cat_name 
       AND (y.item_count > x.item_count OR (y.item_count = x.item_count AND y.cat_id > x.cat_id))
      LEFT
      JOIN 
         ( SELECT a.cat_id, b.*
             FROM categories a
             JOIN 
                ( SELECT cat_name, SUM(item_count) new_count, MAX(item_count) max_count FROM categories GROUP BY cat_name) b
               ON b.cat_name = a.cat_name
         ) z
        ON z.cat_name = x.cat_name

     WHERE
     y.cat_id IS NULL) sourceX

     SET cat.item_count = sourceX.c, jun.cat_id = sourceX.newcatid
     WHERE cat.cat_id = jun.cat_id and cat.cat_id = sourceX.oldcatid

【讨论】:

是的,改编可以更漂亮 我已经测试了你的更新查询,它可以工作。谢谢@Mikey,但我必须感谢@Strawberry。 功劳属于@Strawberry ;)。我只是想看看这一切是否都适用于单个查询:)【参考方案4】:

我认为最好一步一步做你想做的事:

首先,获取你需要的数据:

SELECT Max(`cat_id`), sum(`item_count`) FROM `Categories` GROUP BY `cat_name`

使用这些数据,您将能够检查更新是否正确完成。

然后,对获取的数据进行循环,更新:

update Categories set item_count =
    (
    Select Tot FROM (
        Select sum(`item_count`) as Tot
        FROM `Categories`
        WHERE `cat_name` = '@cat_name') as tmp1
    )
WHERE cat_id = (
    Select MaxId
    FROM (
        select max(cat_id) as MaxId
        FROM Categories
        WHERE `cat_name` = '@cat_name') as tmp2)

注意,如果你运行两次这段代码,结果将是错误的。

最后,将其他ID设置为0

UPDATE Categories set item_count = 0
WHERE `cat_name` = '@cat_name'
AND cat_id <> (
    Select MaxId
    FROM (
        select max(cat_id) as MaxId
        FROM items
        WHERE `cat_name` = '@cat_name0') as tmp2)

【讨论】:

@kolunar 你为什么这么认为?我测试过了 您的第二次更新不起作用,子查询中的items应该是Categories?即使这样,结果仍然是错误的。

以上是关于MySQL更新连接查询以解决重复值的主要内容,如果未能解决你的问题,请参考以下文章

查询以连接 MySQL 中的两个 json 数组?

将连接查询中的重复值限制为仅显示第一个实例

MySQL 左连接时,查询条件查询某个字段为空或者为特定值解决方案

MySQL 左连接时,查询条件查询某个字段为空或者为特定值解决方案

MYSQL UPDATE 查询多个值

mysql_数据查询_连接查询