MySQL比较行和获取更改

时间:2017-02-15 18:36:27

标签: mysql

除了选择和加入之外,还有其他任何东西都会丢失,需要帮助。我有一个表来维护创建的产品的属性。该表中目前有110k行。我正在寻找一种方法来查询数据并返回与每种产品属性变化相关的数据。

+-----------+---------+--------+--------+--------+
| attrib_id | prod_id | height | weight | length |
+-----------+---------+--------+--------+--------+
|         1 |     120 |     20 |      3 |      5 |
|         2 |     101 |      5 |     10 |     20 |
|         3 |     101 |      5 |     10 |     20 |
|         4 |     101 |      5 |     10 |     20 |
|         5 |     120 |     20 |      3 |      5 |
|         6 |     101 |      8 |     10 |     20 |
|         7 |     120 |     20 |      3 |      5 |
|         8 |     101 |      8 |     15 |     30 |
|         9 |     101 |     16 |     15 |     20 |
|        10 |     120 |     20 |     10 |      3 |
+-----------+---------+--------+--------+--------+

我希望在产品属性发生变化时将此类视为输出:

+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
| attrib_id | prod_id | orig_height | new_height | chg_height | orig_weight | new_weight | chg_weight | orig_length | new_length | chg_length |
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
|         6 |     101 |           5 |          8 |          3 |          10 |            |            |          20 |            |            |
|        10 |     120 |          20 |            |            |           3 |         10 |          7 |           5 |          3 |         -2 |
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+

3 个答案:

答案 0 :(得分:1)

您的预期输出有点不正确。

您想要找到最小值和最大值attrib_id,然后使用聚合来查找所需的值:

select attrib_id,
    prod_id,
    original_height,
    case when original_height = new_height then null else new_height end new_height,
    nullif(new_height - original_height, 0) chg_height,
    original_weight,
    case when original_weight = new_weight then null else new_weight end new_weight,
    nullif(new_weight - original_weight, 0) chg_weight,
    original_length,
    case when original_length = new_length then null else new_length end new_length,
    nullif(new_length - original_length, 0) chg_length
from (
    select t2.max_id attrib_id,
        t.prod_id,
        max(case when t.attrib_id = t2.min_id then t.height end) original_height,
        max(case when t.attrib_id = t2.max_id then t.height end) new_height,
        max(case when t.attrib_id = t2.min_id then t.weight end) original_weight,
        max(case when t.attrib_id = t2.max_id then t.weight end) new_weight,
        max(case when t.attrib_id = t2.min_id then t.length end) original_length,
        max(case when t.attrib_id = t2.max_id then t.length end) new_length
    from t
    join (
        select prod_id,
            min(attrib_id) min_id,
            max(attrib_id) max_id
        from t
        group by prod_id
        ) t2 on t.prod_id = t2.prod_id
        and t.attrib_id in (t2.min_id, t2.max_id)
    group by t.prod_id
    ) t;

Demo

答案 1 :(得分:1)

好的,我会建议一个完全不同的方法。我想到了在你的问题中读到“当产品属性发生变化时”的字样。其他答案每次重新计算所有连接和聚合,而您的表t本质上是一个历史日志,它必然会增长和增长,并且您的查询将变得越来越慢。我的方法是创建一个表report并通过触发器使其保持同步。你必须从两个空表开始

DROP TABLE IF EXISTS t;
CREATE TABLE t (attrib_id INT, prod_id INT, height INT, weight INT, length INT);

DROP TABLE IF EXISTS report;
CREATE TABLE report (
   attrib_id INT,  prod_id INT,  
   orig_height INT, new_height INT, chg_height INT,  
   orig_weight INT, new_weight INT, chg_weight INT,  
   orig_length INT, new_length INT, chg_length INT
);

然后,定义触发器:

DROP TRIGGER IF EXISTS trig;
DELIMITER $$
CREATE TRIGGER trig AFTER INSERT ON t FOR EACH ROW BEGIN
  DECLARE old_prod_id, old_height, old_weight, old_length INT;
  SELECT prod_id, new_height, new_weight, new_length 
    INTO old_prod_id, old_height, old_weight, old_length 
    FROM report 
   WHERE prod_id = NEW.prod_id;
  IF ISNULL(old_prod_id) THEN
    INSERT INTO report(attrib_id, prod_id, orig_height, orig_weight, orig_length)
        VALUES (NEW.attrib_id, NEW.prod_id, NEW.height, NEW.weight, NEW.length);
  ELSEIF old_height != NEW.height OR old_weight != NEW.weight OR old_length != NEW.length 
        OR ISNULL(old_height) -- First change: I suppose checking one field is enough 
        THEN
    UPDATE report SET 
      attrib_id = NEW.attrib_id,
      new_height = NEW.height, chg_height = NEW.height - orig_height,
      new_weight = NEW.weight, chg_weight = NEW.weight - orig_weight,
      new_length = NEW.length, chg_length = NEW.length - orig_length
     WHERE prod_id = NEW.prod_id;
  END IF;
END$$
DELIMITER ;

当您使用您提供给我们的值填充t时,您会得到:

> SELECT * FROM report;
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
| attrib_id | prod_id | orig_height | new_height | chg_height | orig_weight | new_weight | chg_weight | orig_length | new_length | chg_length |
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+
|        10 |     120 |          20 |         20 |          0 |           3 |         10 |          7 |           5 |          3 |         -2 |
|         9 |     101 |           5 |         16 |         11 |          10 |         15 |          5 |          20 |         20 |          0 |
+-----------+---------+-------------+------------+------------+-------------+------------+------------+-------------+------------+------------+

您的情况会更加灵活,您可以根据需要轻松微调SELECT ... FROM report查询。

答案 2 :(得分:0)

我不确定这是你在找什么,但它跟踪所有的变化:

select attrib_id, prod_id, 
       height as ori_height, nheight as new_height, (nheight - height) as chg_height,  
       weight as ori_weight, nweight as new_weight, (nweight - weight) as chg_weight,
       length as ori_length, nlength as new_length, (nlength - length) as chg_length
from (
        select attr1.attrib_id, attr1.prod_id, attr1.height, attr1.weight, attr1.length
                ,(select attr2.height from attr attr2 
                  where attr2.prod_id = attr1.prod_id and attr2.attrib_id > attr1.attrib_id limit 1) nheight
                ,(select attr2.weight from attr attr2 
                  where attr2.prod_id = attr1.prod_id and attr2.attrib_id > attr1.attrib_id limit 1) nweight
                ,(select attr2.length from attr attr2 
                  where attr2.prod_id = attr1.prod_id and attr2.attrib_id > attr1.attrib_id limit 1) nlength
        from attr attr1
        order by attr1.prod_id, attr1.attrib_id
     ) calc
;

它会逐行返回所有更改:

+-----------+---------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
| attrib_id | prod_id | ori_height | new_height | chg_height | ori_weight | new_weight | chg_weight | ori_length | new_length | chg_length |
+-----------+---------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
| 2         | 101     | 5          | 5          | 0          | 10         | 10         | 0          | 20         | 20         | 0          |
| 3         | 101     | 5          | 5          | 0          | 10         | 10         | 0          | 20         | 20         | 0          |
| 4         | 101     | 5          | 8          | 3          | 10         | 10         | 0          | 20         | 20         | 0          |
| 6         | 101     | 8          | 8          | 0          | 10         | 15         | 5          | 20         | 30         | 10         |
| 8         | 101     | 8          | 16         | 8          | 15         | 15         | 0          | 30         | 20         | -10        |
| 9         | 101     | 16         | NULL       | NULL       | 15         | NULL       | NULL       | 20         | NULL       | NULL       |
+-----------+---------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
| 1         | 120     | 20         | 20         | 0          | 3          | 3          | 0          | 5          | 5          | 0          |
| 5         | 120     | 20         | 20         | 0          | 3          | 3          | 0          | 5          | 5          | 0          |
| 7         | 120     | 20         | 20         | 0          | 3          | 10         | 7          | 5          | 3          | -2         |
| 10        | 120     | 20         | NULL       | NULL       | 10         | NULL       | NULL       | 3          | NULL       | NULL       |
+-----------+---------+------------+------------+------------+------------+------------+------------+------------+------------+------------+