Assessments table:

+---------------+-----------+---------------------+
| assessment_id | device_id |     created_at      |
+---------------+-----------+---------------------+
|             1 |       1   | 2022-07-15 20:03:03 |
|             2 |       2   | 2022-07-15 21:03:03 |
|             3 |       1   | 2022-07-15 22:03:03 |
|             4 |       2   | 2022-07-15 23:03:03 |
|             5 |       2   | 2022-07-15 23:03:03 |
+---------------+-----------+---------------------+

Results table:

+---------------+---------+--------+
| assessment_id | test    | result |
+---------------+---------+--------+
|             1 |       A | PASS   |
|             2 |       B | FAIL   |
|             3 |       A | FAIL   |
|             4 |       B | PASS   |
|             5 |       B | PASS   |
+---------------+---------+--------+

Objective

每次test_id中的result个改变时,我都要返回一行.

例如,设备1具有判断1和3.判断1中的测试A通过,判断3中的测试A失败,因此我们希望将此更改作为一行返回.

设备2具有判断2、4和5.判断2和4中的测试结果发生了更改(测试B从失败更改为通过),我们想为此返回一行.

我们不想为判断5返回一行,因为测试B通过了,并且在判断4中也通过了.没有变化.

生成的表如下所示:


+-----------+---------+------------------------+----------------+----------------------+--------------------+------------+----------------------+
| device_id | test_id | previous_assessment_id | previous_value | previous_value_date  | next_assessment_id | next_value |   next_value_date    |
+-----------+---------+------------------------+----------------+----------------------+--------------------+------------+----------------------+
|         1 | A       |                      1 | PASS           | 15/07/2022  20:03:03 |                  3 | FAIL       | 15/07/2022  22:03:03 |
|         2 | B       |                      2 | FAIL           | 15/07/2022  21:03:03 |                  4 | PASS       | 15/07/2022  23:03:03 |
+-----------+---------+------------------------+----------------+----------------------+--------------------+------------+----------------------+

我试过处理几个查询,找到了here on SO个,但它们要么花费了很长时间并返回了错误的数据,要么根本不起作用.我不认为这是重复的,因为我使用了多个表,而我看到的每个其他问题都涉及一个表.

我也看了this SO question个,但没有得到适用于我的情况的有用答案.

我在使用SQL Fiddle时遇到了一些奇怪的问题,但下面是我一直在修补的测试模式:

CREATE TABLE `assessments` (
  `id` int,
  `device_id` int,
  `created_at` datetime
);

INSERT INTO `so_assessments` (`id`, `device_id`, `created_at`) VALUES (1, 1, '2022-07-09 22:56:00');
INSERT INTO `so_assessments` (`id`, `device_id`, `created_at`) VALUES (2, 2, '2022-07-10 22:56:06');
INSERT INTO `so_assessments` (`id`, `device_id`, `created_at`) VALUES (3, 1, '2022-07-11 22:56:11');
INSERT INTO `so_assessments` (`id`, `device_id`, `created_at`) VALUES (4, 2, '2022-07-12 22:56:17');
INSERT INTO `so_assessments` (`id`, `device_id`, `created_at`) VALUES (5, 2, '2022-07-13 22:56:24');

CREATE TABLE `results` (
  `assessment_id` int,
  `test` enum('A','B'),
  `result` enum('PASS','FAIL')
);

INSERT INTO `results` (`assessment_id`, `test`, `result`) VALUES (1, 'A', 'PASS');
INSERT INTO `results` (`assessment_id`, `test`, `result`) VALUES (2, 'B', 'FAIL');
INSERT INTO `results` (`assessment_id`, `test`, `result`) VALUES (3, 'A', 'FAIL');
INSERT INTO `results` (`assessment_id`, `test`, `result`) VALUES (4, 'B', 'PASS');
INSERT INTO `results` (`assessment_id`, `test`, `result`) VALUES (5, 'B', 'PASS');

推荐答案

如果您使用的是MySQL 8,窗口函数会有所帮助.https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html

您可以将your results除以devicetest,并添加一列作为result的上一个值,然后使用结果与上一个值不同的最后一行.

以下查询在结果中创建一个新列,其中包含previous_value

SELECT 
    assessment_id,
    device_id,
    test,
    result,
    LAG (result) over w as `previous_value`,
    LAG (assessment_id) over w as `previous_assessment_id`
FROM assessments join results using(assessment_id)
WINDOW w AS (PARTITION BY device_id, test ORDER BY assessment_id)

得出结果:

+---------------+-----------+------+--------+----------------+------------------------+
| assessment_id | device_id | test | result | previous_value | previous_assessment_id |
+---------------+-----------+------+--------+----------------+------------------------+
|             1 |         1 | A    | PASS   | NULL           |                   NULL |
|             3 |         1 | A    | FAIL   | PASS           |                      1 |
|             2 |         2 | B    | FAIL   | NULL           |                   NULL |
|             4 |         2 | B    | PASS   | FAIL           |                      2 |
|             5 |         2 | B    | PASS   | PASS           |                      4 |
+---------------+-----------+------+--------+----------------+------------------------+

这是战斗的重要部分.现在,我们需要获取该结果,并找到每个设备/测试对具有最高判断id的行,其中result!=上一个_值.

该窗口是在GROUP BYORDER BY甚至HAVING之后计算的,因此在该查询(我已经想到)中可以做的事情不多,可以将其缩小到每个设备/测试对的最近条目.因此,上述内容必须是一个子查询才能获得最终结果.

注意:我假设如果结果从未改变,那么您希望显示第一次记录结果的时间.换句话说,您希望以previous_value = NULL作为一个过渡来计算结果.

以下是一个查询,列出了设备/测试对的测试结果更改的所有时间:

SELECT * FROM 
(SELECT 
    assessment_id,
    device_id,
    test,
    result,
    LAG (result) over w as `previous_value`
FROM assessments join results using(assessment_id)
WINDOW w AS (PARTITION BY `device_id`, `test` ORDER BY `assessment_id`)
) AS t
WHERE result != `previous_value` OR `previous_value` IS NULL

得到结果(我省略了previous_assesssment_id个,其他的留了一些空白):

+---------------+-----------+------+--------+----------------+
| assessment_id | device_id | test | result | previous_value |
+---------------+-----------+------+--------+----------------+
|             1 |         1 | A    | PASS   | NULL           |
|             3 |         1 | A    | FAIL   | PASS           |
|             2 |         2 | B    | FAIL   | NULL           |
|             4 |         2 | B    | PASS   | FAIL           |
+---------------+-----------+------+--------+----------------+

EDIT

这就是问题的答案.如果第一次设置值不重要,只需删除WHERE子句的OR部分.这个答案的其余部分是因为我说服自己,问题是得到价值翻转的最近时间.我把它留在这里,但只是出于兴趣.

Carrying On

这是结果与之前不同的所有时间,加上第一次记录结果.差不多了.

此时,在外部查询中添加另一个窗口来聚合上面的行并识别正确的行是很诱人的.但至少在MySQL 8中,不支持嵌套窗口.

但是考虑到这个结果,我们可以使用MAX()GROUP BY创建一个查询,该查询给出了我们最终想要的所有行的判断ID:

SELECT MAX(assessment_id)
FROM (
    SELECT 
       assessment_id,
       device_id,
       test,
       result,
       LAG (result) over w as `previous_value`,
       LAG (assessment_id) over w as `previous_assessment_id`
    FROM assessments join results using(assessment_id)
    WINDOW w AS (PARTITION BY device_id, test ORDER BY assessment_id)
) AS t

where result != previous_value OR previous_value IS NULL
GROUP BY device_id, test

这将产生:

+--------------------+
| MAX(assessment_id) |
+--------------------+
|                  3 |
|                  4 |
+--------------------+

现在我们确切地知道需要哪一行;但是我们构建了所有关于先前值的数据,现在我们需要一种方法将该查询的结果与子查询的结果连接起来.

幸运的是,MySQL 8有一种方法可以隐藏查询并多次使用它,称为公共表表达式,使用WITH子句docs here.因此,我们可以创建包含所有有趣数据的表,然后将其用作子查询,以获取我们最终想要的id,然后将其与我们刚刚创建的结果连接起来:

WITH
  transitions AS (SELECT 
    assessment_id,
    device_id,
    test,
    result,
    LAG (result) over w as `previous_value`,
    LAG (assessment_id) over w as `previous_assessment_id`
FROM assessments join results using(assessment_id)
WINDOW w AS (PARTITION BY device_id, test ORDER BY assessment_id)
)

SELECT transitions.*
FROM transitions
JOIN (
    SELECT MAX(assessment_id) as assessment_id
    FROM transitions
    WHERE result != previous_value OR previous_value IS NULL
    GROUP BY device_id, test
) AS t2 using (assessment_id)

给我们最终答案(以及您可以填写的其他栏):

+---------------+-----------+------+--------+----------------+------------------------+
| assessment_id | device_id | test | result | previous_value | previous_assessment_id |
+---------------+-----------+------+--------+----------------+------------------------+
|             3 |         1 | A    | FAIL   | PASS           |                      1 |
|             4 |         2 | B    | PASS   | FAIL           |                      2 |
+---------------+-----------+------+--------+----------------+------------------------+

第一部分创建了一个数据集,其中包括关于每次测试结果的所有信息.然后我们编写一个查询,获取该查询中感兴趣行的id,然后我们连接回原始数据集以填充所有列.

Mysql相关问答推荐

MySQL查询获取图书的唯一读取页面数

为什么我安装MySQL时不能使用3306端口?

在MySQL和JavaFX中保存和检索图像文件

使用复合主键更新后的MySQL触发器失败

计算同一个表中的两列,然后将两者都算作总数

Select 构成每天聚合 MAX 的各个行

实体内约束的唯一增量 ID 生成

时间戳上滚动窗口的 SQL 计数不同

如何使用mysql更新列中的json数据

仅计算 DATEDIFF (MySQL) 中的工作日

如何在 mysql 中获取 Day Wise 的平均值?

使用来自另一个表 mysql 的值将新行插入到表中

在一个 SQL 查询中对同一列中相同类型的不同值求和

使用带有 ELSEIF 和 ELSE 的 3 列更新问题

什么是 PyMySQL,它与 MySQLdb 有何不同?它会影响 Django 部署吗?

Python MYSQL 更新语句

INSERT ... 使用 WHERE 进行重复的密钥更新?

仅在 MYSQL DATEDIFF 中显示小时数

MySQL 在每个唯一值第一次出现时 Select 行

mysql错误'TYPE = MyISAM'