简体   繁体   中英

Consolidate two rows in a SQL Table based on timestamp and an boolean

Out of "historic" reason i have following table:

CREATE TABLE IF NOT EXISTS `measurement`
(
        `measureID`     int NOT NULL AUTO_INCREMENT ,
        `terraID`       int NOT NULL ,
        `pinID`         int NOT NULL ,
        `time`          timestamp NOT NULL ,
        `value`         float NULL ,
        `temperature`   boolean NOT NULL ,

        PRIMARY KEY (`measureID`),
        KEY `measure_terraID_FK` (`terraID`),
        KEY `measure_pinID_FK` (`pinID`),
        CONSTRAINT `measure_terraID_reference` FOREIGN KEY `measure_terraID_FK` (`terraID`) REFERENCES `terrarium` (`terraID`),
        CONSTRAINT `measure_pinID_reference` FOREIGN KEY `measure_pinID_FK` (`pinID`) REFERENCES `pins` (`pinID`)
) ENGINE=INNODB COLLATE=utf8mb4_unicode_ci;
| measureID | terraID | pinID | time                | value  | temperature |
|:----------|---------|-------|---------------------|--------|------------:|
|         1 |       1 |     9 | 2020-04-10 13:00:01 |   34.3 |           0 |
|         2 |       1 |     9 | 2020-04-10 13:00:01 |   26.5 |           1 |
|         3 |       2 |    10 | 2020-04-10 13:00:01 |   35.1 |           0 |
|         4 |       2 |    10 | 2020-04-10 13:00:01 |   32.9 |           1 |
|         5 |       1 |     9 | 2020-04-10 13:05:01 |   34.4 |           0 |
|         6 |       1 |     9 | 2020-04-10 13:05:01 |   26.6 |           1 |
|         7 |       2 |    10 | 2020-04-10 13:05:01 |     35 |           0 |
|         8 |       2 |    10 | 2020-04-10 13:05:01 |     33 |           1 |
[...]
|     38087 |       2 |    10 | 2020-08-31 12:50:02 |   35.9 |           0 |
|     38088 |       2 |    10 | 2020-08-31 12:50:02 |     35 |           1 |
|     38089 |       1 |    11 | 2020-08-31 12:50:02 | 25.187 |           1 |
|     38090 |       2 |    12 | 2020-08-31 12:50:02 | 28.312 |           1 |
|     38091 |       2 |    10 | 2020-08-31 12:55:01 |   35.8 |           0 |
|     38092 |       2 |    10 | 2020-08-31 12:55:01 |     35 |           1 |
|     38093 |       1 |    11 | 2020-08-31 12:55:01 |  25.25 |           1 |
|     38094 |       2 |    12 | 2020-08-31 12:55:01 | 28.375 |           1 |

and want that to be inserted into a new table, based on time and pinID, so that the final table looks like that:

CREATE TABLE IF NOT EXISTS `measurement`
(
        `measureID`     int NOT NULL AUTO_INCREMENT ,
        `terraID`       int NOT NULL ,
        `pinID`         int NOT NULL ,
        `time`          timestamp NOT NULL ,
        `temperature`   float NULL ,
        `humidity`      float NULL ,

        PRIMARY KEY (`measureID`),
        KEY `measure_terraID_FK` (`terraID`),
        KEY `measure_pinID_FK` (`pinID`),
        CONSTRAINT `measure_terraID_reference` FOREIGN KEY `measure_terraID_FK` (`terraID`) REFERENCES `terrarium` (`terraID`),
        CONSTRAINT `measure_pinID_reference` FOREIGN KEY `measure_pinID_FK` (`pinID`) REFERENCES `pins` (`pinID`)
) ENGINE=INNODB COLLATE=utf8mb4_unicode_ci;
| measureID | terraID | pinID | time                | temperature | humidity |
|:----------|---------|-------|---------------------|--------------|---------:|
|         2 |       1 |     9 | 2020-04-10 13:00:01 |         26.5 |     34.3 |
|         4 |       2 |    10 | 2020-04-10 13:00:01 |         32.9 |     35.1 |
|         6 |       1 |     9 | 2020-04-10 13:05:01 |         26.6 |     34.4 |
|         8 |       2 |    10 | 2020-04-10 13:05:01 |           33 |       35 |
[...]
|     38088 |       2 |    10 | 2020-08-31 12:50:02 |           35 |     35.9 |
|     38089 |       1 |    11 | 2020-08-31 12:50:02 |       25.187 |     NULL |
|     38090 |       2 |    12 | 2020-08-31 12:50:02 |       28.312 |     NULL |
|     38092 |       2 |    10 | 2020-08-31 12:55:01 |           35 |     35.8 |
|     38093 |       1 |    11 | 2020-08-31 12:55:01 |        25.25 |     NULL |
|     38094 |       2 |    12 | 2020-08-31 12:55:01 |       28.375 |     NULL |

My SQL Version is mariadb-10.3

I don't want to lose any values and I don't care for the temperature column, because it's an old identifier in how to read the value. But I want to modify all old entries to be transformed to the new structure. But you can use it in IF(temperature=1, value, NULL) as temperature, IF(temperature=0, value, '') as humidity or somewhat.

The terraID and pinID are keyed to other tables and not all sensors deliver temperature and humidity.

I have no luck with some insert into select subquerie magic... maybe I'm too dumb to wrap my head around the problem.

Can you possibly lead me to the correct function. It's just a once-triggered manual job.

If I follow you correctly, you can use conditional aggregation:

select 
    min(measureID), 
    terraID, 
    pinID, 
    time, 
    max(case when temperature = 1 then value end) value,
    max(case when temperature = 0 then value end) humidity
from mytable
group by terraID, pinID, time
order by min(measureID)

If needed, you can easily turn this to an insert query (using insert... select... ) or even to a create table statement ( create table newtable as... ).

Demo on DB Fiddle :

measureID | terraID | pinID | time                |  value | humidity
--------: | ------: | ----: | :------------------ | -----: | -------:
        2 |       1 |     9 | 2020-04-10 13:00:01 | 26.500 |   34.300
        4 |       2 |    10 | 2020-04-10 13:00:01 | 32.900 |   35.100
        6 |       1 |     9 | 2020-04-10 13:05:01 | 26.600 |   34.400
        8 |       2 |    10 | 2020-04-10 13:05:01 | 33.000 |   35.000
    38088 |       2 |    10 | 2020-08-31 12:50:02 | 35.000 |   35.900
    38089 |       1 |    11 | 2020-08-31 12:50:02 | 25.187 |     null
    38090 |       2 |    12 | 2020-08-31 12:50:02 | 28.312 |     null
    38092 |       2 |    10 | 2020-08-31 12:55:01 | 35.000 |   35.800
    38093 |       1 |    11 | 2020-08-31 12:55:01 | 25.250 |     null
    38094 |       2 |    12 | 2020-08-31 12:55:01 | 28.375 |     null

Use LEAD() window function:

INSERT INTO new_measurement(measureID, terraID, pinID, time, temperature, humidity)
SELECT measureID, terraID, pinID, time, 
       CASE temperature WHEN 1 THEN value END, 
       value
FROM (
  SELECT *, 
         LEAD(terraID) OVER (ORDER BY time DESC) next_terraID,
         LEAD(pinID) OVER (ORDER BY time DESC) next_pinID
  FROM measurement 
) t  
WHERE terraID <> next_terraID OR pinID <> next_pinID

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM