【问题标题】:MySQL 5.5 - Select rows where record changesMySQL 5.5 - 选择记录更改的行
【发布时间】:2019-02-01 11:14:55
【问题描述】:

我有这张表,我需要生成包含最后一个 A 和第一个 B 以及最后一个 B 和第一个 A 的行。预期结果在这里:

    ID | Date       | OPS
     6 | 2018-12-20 | A
     7 | 2018-12-20 | B
    11 | 2018-12-24 | B
    12 | 2018-12-25 | A

我试图用零运气来处理下面的代码。 我不能使用分析函数,例如 ROW_NUMBER()、LEAD () 或 LAG ()。

SELECT *
FROM T1
JOIN (
    SELECT COUNT(*) cnt, t1.OPS
    FROM (
        SELECT DISTINCT ts1.OPS
        FROM T1 ts1
        JOIN T1 tx1 ON ts1.OPS = tx1.OPS
    ) t1
    JOIN (
        SELECT DISTINCT ts2.OPS
        FROM T1 ts2
        JOIN T1 tx2 ON ts2.OPS = tx2.OPS
    ) t2 on t1.OPS <= t2.OPS
    GROUP BY t1.OPS
) tt ON T1.OPS = tt.OPS

我的桌子是:

CREATE TABLE t1(
    ID int,
    Date1 date,
    OPS CHAR
);
INSERT INTO T1 VALUES
( 1, '2018-12-17', 'A'),
( 2, '2018-12-18', 'A'),
( 3, '2018-12-19', 'A'),
( 4, '2018-12-19', 'A'),
( 5, '2018-12-19', 'A'),
( 6, '2018-12-20', 'A'),
( 7, '2018-12-20', 'B'),
( 8, '2018-12-21', 'B'),
( 9, '2018-12-22', 'B'),
(10, '2018-12-23', 'B'),
(11, '2018-12-24', 'B'),
(12, '2018-12-25', 'A'),
(13, '2018-12-26', 'A'),
(14, '2018-12-27', 'A'),
(15, '2018-12-28', 'A');

【问题讨论】:

    标签: mysql sql window-functions


    【解决方案1】:

    没有LEADLAG 并且考虑到您每个日期有多个记录,您可以使用可怕 简化解决方案假设记录按id 排序日期。它不需要 id 是连续的,并且适用于两个以上的 ops 值:

    SELECT *
    FROM t AS ref
    WHERE EXISTS (
        SELECT 1
        FROM t AS lag
        WHERE ops <> ref.ops
        AND id < ref.id
        AND NOT EXISTS (
            SELECT 1
            FROM t AS tmp
            WHERE id > lag.ID
            AND id < ref.id
        )
    ) OR EXISTS (
        SELECT 1
        FROM t AS led
        WHERE ops <> ref.ops
        AND id > ref.id
        AND NOT EXISTS (
            SELECT 1
            FROM t AS tmp
            WHERE id < led.ID
            AND id > ref.id
        )
    )
    

    基本上,对于每一行,查询都会搜索之前所有 id 不匹配的行,然后确定其间是否存在一行。它同样向前搜索。

    【讨论】:

    • 它给出'表'test.t'不存在'
    • 显然。用你的表名替换t
    【解决方案2】:

    尝试以下逻辑,使用一系列联合来表达您的问题:

    (SELECT ID, Date1, OPS FROM T1 WHERE OPS = 'B' ORDER BY Date1 LIMIT 1)
    
    UNION ALL
    
    (SELECT ID, Date1, OPS FROM T1
    WHERE OPS = 'A' AND Date1 <= (SELECT MIN(Date1) FROM T1 WHERE OPS = 'B')
    ORDER BY Date1 DESC LIMIT 1)
    
    UNION ALL
    
    (SELECT ID, Date1, OPS FROM T1 WHERE OPS = 'B' ORDER BY Date1 DESC LIMIT 1)
    
    UNION ALL
    
    (SELECT ID, Date1, OPS FROM T1
    WHERE OPS = 'A' AND Date1 >= (SELECT MAX(Date1) FROM T1 WHERE OPS = 'B')
    ORDER BY Date1 LIMIT 1)
    
    ORDER BY Date1, OPS;
    

    Demo

    请注意,这是一个间隙和孤岛问题。如果不使用您似乎没有的分析函数,这些将很难处理。如果您使用的是 MySQL 8+,那么我们可以编写一个看起来更简洁的查询。

    【讨论】:

    • 代码中没有 LIMIT 的任何方式。除此之外,它就像魔术一样
    • @Kalenji 是的,我可以在不使用LIMIT 的情况下编写上述内容,但是查询会更加冗长和丑陋。真的,如果你需要这种类型的查询,你应该认真升级到 MySQL 8+,然后使用分析函数。
    【解决方案3】:

    如果我们允许使用 max 和 min 函数,那么很容易单独计算这 4 个并将单个结果合并。

    -- 选择最后一个 A 选择 ID、Date1、OPS 来自 test_t1 其中 test_t1.OPS = 'A' 和 test_t1.Date1 在 ( 选择 max(t1_a.Date1) 作为 Date1 从 (选择 ID、Date1、OPS 来自 test_t1 其中 OPS = 'A') t1_a 内连接 (选择 min(Date1) 作为 Date1 来自 test_t1 其中 OPS = 'B') t1_min_b 在 t1_a.Date1

    -- 选择第一个 B

    选择 ID、Date1、OPS 来自 test_t1 其中 test_t1.OPS = 'B' 和 test_t1.Date1 在 ( 选择 min(Date1) 作为 Date1 来自 test_t1 其中 OPS = 'B' ) 联盟 /* 选择最后一个 B / 选择 ID、Date1、OPS 来自 test_t1 其中 test_t1.OPS = 'B' 和 test_t1.Date1 在 ( 选择 max(Date1) 作为 Date1 来自 test_t1 其中 OPS = 'B' ) 联盟 / 选择第一个 A */ 选择 ID、Date1、OPS 来自 test_t1 其中 test_t1.OPS = 'A' 和 test_t1.Date1 在 ( 选择 min(t1_a.Date1) 作为 Date1 从 (选择 ID、Date1、OPS 来自 test_t1 其中 OPS = 'A') t1_a 内连接 (选择 max(Date1) 作为 Date1 来自 test_t1 其中 OPS = 'B') t1_max_b 在 t1_a.Date1 >= t1_max_b.Date1 ) ;

    【讨论】:

    • 它给出了 ERROR 1064 (42000):
    • 从你的表名 t1 中替换 test_t1 表名并替换或删除以 -- 和 /* 组合开头的 cmets
    【解决方案4】:

    您可以使用相关子查询来计算每个操作的最小和最大 ID,例如

    SELECT t.*
    FROM T1 t
    WHERE
        t.id IN (
            SELECT MIN(id) FROM T1 WHERE ops = t.ops
            UNION ALL SELECT MAX(id) FROM T1 WHERE ops = t.ops
        )
    ORDER BY t.ops, t.id DESC
    

    【讨论】:

    • 它给出 ERROR 1241 (21000): Operand should contain 1 column(s)
    • @Kalenji:查询已更新,请告诉我它是否适合您?
    猜你喜欢
    • 2019-02-14
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 1970-01-01
    • 2011-11-07
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多