NWOWN
NWOWN

Reputation: 407

Value Assign and Use IF-ELSE in SELECT Query in MySQL

Example table

| 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

When the value of A, B, C is changed, it is saved.

So example table, '2020-11-19 11:11:34' save only B, because A and C is not change on '2020-11-19 11:10:13'

I want get select A, B, C value group by TIME

Example result is here

| 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

first step, 'Get Value By Time and Name' is done

And first result is like this.

| 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)

It get value only that time.

So I created a stored procedure to get the most recent value before the search time.

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

But it has problem yet...lastA, lastB, lastC is not updated when VALUE is not null

That procedure result is here

| 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 is not 10.3, it is 40.3, because there is a newer value.

But it hasn't changed.

In this section,

    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;

Can I use IF-ELSE like this?

    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;

Is it possible way?

How do I get the results I want?

EDIT) I use MariaDB 10.5

Upvotes: 0

Views: 157

Answers (1)

Akina
Akina

Reputation: 42651

This is 2 separate tasks, not one task:

  1. Find actual value for each name and each time. It has simple solution:
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`
  1. Pivot above data. The name column list is static, so we may use conditional aggregation over the result from previous task:
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

Upvotes: 1

Related Questions