MySQL中SELECT查询中的值分配和使用IF-ELSE

Posted

技术标签:

【中文标题】MySQL中SELECT查询中的值分配和使用IF-ELSE【英文标题】:Value Assign and Use IF-ELSE in SELECT Query in MySQL 【发布时间】:2021-03-03 14:08:56 【问题描述】:

示例表

| TIME                 | NAME  |     VALUE
-----------------------+-------+---------------------
2020-11-19 11:10:13    |   A   |     12.3
2020-11-19 11:10:13    |   B   |     14.1
2020-11-19 11:10:13    |   C   |     10.3
2020-11-19 11:11:34    |   B   |     21.3
2020-11-19 11:12:45    |   A   |     32.1
2020-11-19 11:12:45    |   C   |     40.3
2020-11-19 11:13:05    |   A   |     13.1

当A、B、C的值改变时,保存。

所以示例表“2020-11-19 11:11:34”只保存 B,因为 A 和 C 在“2020-11-19 11:10:13”上没有变化

我想按时间选择 A、B、C 值组

示例结果在这里

| TIME                 | A     | B     | C     |
-----------------------+-------+-------+-----------
2020-11-19 11:10:13    | 12.3  | 14.1  | 10.3
2020-11-19 11:11:34    | 12.3  | 21.3  | 10.3
2020-11-19 11:12:45    | 32.1  | 21.3  | 40.3
2020-11-19 11:13:05    | 13.1  | 21.3  | 40.3

第一步,“按时间和名称获取价值”已完成

第一个结果是这样的。

| TIME                 | A     | B     | C     |
-----------------------+-------+-------+-----------
2020-11-19 11:10:13    | 12.3  | 14.1  | 10.3
2020-11-19 11:11:34    |(NULL) | 21.3  | (NULL)
2020-11-19 11:12:45    | 32.1  |(NULL) | 40.3
2020-11-19 11:13:05    | 31.1  |(NULL) | (NULL)

只有那个时候才有价值。

所以我创建了一个存储过程来获取搜索时间之前的最新值。

CREATE PROCEDURE `GetValuesByTime`(
    IN `StartDate` DATETIME,
    IN `EndDate` DATETIME
)
BEGIN       

    DECLARE lastA DOUBLE;
    DECLARE lastB DOUBLE;
    DECLARE lastC DOUBLE;
    
    SET @lastA = 0;
    SET @lastB = 0;
    SET @lastC = 0;

    SELECT VALUE 
    INTO lastA
    FROM data_log 
    WHERE TIME <= StartDate order by TIME desc LIMIT 1;

    SELECT VALUE 
    INTO lastB
    FROM data_log 
    WHERE TIME <= StartDate order by TIME desc LIMIT 1;

    SELECT VALUE 
    INTO lastC
    FROM data_log 
    WHERE TIME <= StartDate order by TIME desc LIMIT 1;
    
    SELECT          
        TIME,
        IFNULL(sum(NAME) = 'A' THEN VALUE END), lastA) AS 'A',
        IFNULL(sum(NAME) = 'B' THEN VALUE END), lastB) AS 'B', 
        IFNULL(sum(NAME) = 'C' THEN VALUE END), lastC) AS 'C',
    FROM data_log
    WHERE TIME >= StartDate AND TIME <= EndDate
    group BY TIME;
END

但还是有问题...当VALUE不为空时,lastA、lastB、lastC不会更新

程序结果在这里

| TIME                 | A     | B     | C     |
-----------------------+-------+-------+-----------
2020-11-19 11:10:13    | 12.3  | 14.1  | 10.3
2020-11-19 11:11:34    | 12.3  | 21.3  | 10.3
2020-11-19 11:12:45    | 32.1  | 14.1  | 40.3
2020-11-19 11:13:05    | 31.1  | 14.1  | 10.3

'2020-11-19 11:13:05' C 不是 10.3,是 40.3,因为有更新的值。

但它没有改变。

在本节中,

    SELECT          
        TIME,
        IFNULL(sum(NAME) = 'A' THEN VALUE END), lastA) AS 'A',
        IFNULL(sum(NAME) = 'B' THEN VALUE END), lastB) AS 'B', 
        IFNULL(sum(NAME) = 'C' THEN VALUE END), lastC) AS 'C',
    FROM data_log
    WHERE TIME >= StartDate AND TIME <= EndDate
    group BY TIME;

我可以这样使用 IF-ELSE 吗?

    SELECT          
        TIME,
        IF (sum(NAME) = 'A' THEN VALUE END) = null THEN  lastA
        ELSE VALUE, lastA := VALUE AS 'A',

        IF (sum(NAME) = 'B' THEN VALUE END) = null THEN  lastB
        ELSE VALUE, lastB := VALUE 

        IF (sum(NAME) = 'C' THEN VALUE END) = null THEN  lastC
        ELSE VALUE, lastC := VALUE 
    FROM data_log
    WHERE TIME >= StartDate AND TIME <= EndDate
    group BY TIME;

有可能吗?

如何获得我想要的结果?

编辑) 我使用 MariaDB 10.5

【问题讨论】:

NAME 列中的值列表是静态的吗? 是的,只有 A、B 和 C。 【参考方案1】:

这是 2 个独立的任务,而不是一个任务:

    查找每个名称和每次的实际值。它有简单的解决方案:
WITH 
cte1 AS ( SELECT DISTINCT name FROM data_log ),
cte2 AS ( SELECT DISTINCT `time` FROM data_log )
SELECT DISTINCT
       cte1.name,
       cte2.`time`,
       FIRST_VALUE(data_log.value) OVER (PARTITION BY cte1.name, cte2.`time` ORDER BY data_log.`time` DESC) value
FROM cte1
CROSS JOIN cte2
LEFT JOIN data_log ON data_log.name = cte1.name
                  AND data_log.`time` <= cte2.`time`
ORDER BY name, `time`
    以数据为中心。 name 列列表是静态的,因此我们可以对上一个任务的结果使用条件聚合:
WITH 
cte1 AS ( SELECT DISTINCT name FROM data_log ),
cte2 AS ( SELECT DISTINCT `time` FROM data_log ),
cte3 AS ( SELECT DISTINCT
                cte1.name,
                cte2.`time`,
                FIRST_VALUE(data_log.value) OVER (PARTITION BY cte1.name, cte2.`time` ORDER BY data_log.`time` DESC) value
           FROM cte1
           CROSS JOIN cte2
           LEFT JOIN data_log ON data_log.name = cte1.name
                             AND data_log.`time` <= cte2.`time` )
SELECT `time`, 
       MAX(CASE WHEN name = 'A' THEN value END) A, 
       MAX(CASE WHEN name = 'B' THEN value END) B, 
       MAX(CASE WHEN name = 'C' THEN value END) C
FROM cte3
GROUP BY `time`

fiddle

【讨论】:

以上是关于MySQL中SELECT查询中的值分配和使用IF-ELSE的主要内容,如果未能解决你的问题,请参考以下文章

mysql语句where条件中的是啥意思

MYSQL Select If Query - WP 自定义字段

按mysql中select语句“in”子句中的值顺序排序

Mysql联合查询UNION和UNION ALL的使用介绍

MySQL使用Select更新查询

使用来自 SELECT 子查询的值的 UPDATE 查询,高效