Home > Blockchain >  Refactor Oracle SQL Query that has many subqueries
Refactor Oracle SQL Query that has many subqueries

Time:09-10

I have a query in an Oracle database that I am hoping to simplify. I have done something similar with a PIVOT before, but not something this complex. I have two tables, WEATHER AND TELEMETRY. The query is for an ancient report that ties several values from several weather readings to the telemetry data. The weather data looks like this:

READING_DATE HOUR_VALUE STATION_ID TEMPERATURE HUMIDITY WIND CONDITIONS
08/01/2022 1 station1 77 50 1 clear
08/01/2022 1 station2 75 50 0 clear
08/01/2022 1 station3 74 60 3 overcast
08/01/2022 2 station1 76 50 3 clear
08/01/2022 2 station2 74 50 0 clear
08/01/2022 2 station3 70 65 2 overcast
08/01/2022 3 station1 73 60 5 cloudy
08/01/2022 3 station2 71 70 2 cloudy
08/01/2022 3 station3 69 100 3 rain

The telemetry table looks like this:

READING_DATE HOUR_VALUE TELEMETRY_VALUE_1 TELEMETRY_VALUE_2
08/01/2022 1 430 10
08/01/2022 2 405 9
08/01/2022 3 390 8

The telemetry values are only tied to the date and hour, not to the station ids. The desired result is a query that lays out the temperature, wind, humidity, and conditions for each hour as shown below (the temp, wind, humidity and conditions repeat for up to 10 stations, but I cut it off after two here):

READING_DATE HOUR_VALUE TELEMETRY_VALUE_1 C1_TEMP C1_WIND C1_HUMIDITY C1_CONDITIONS C2_TEMP C2_WIND C2_HUMIDITY C2_CONDITIONS
08/01/2022 1 430 77 1 50 clear 75 0 50 clear
08/01/2022 2 405 76 3 50 clear 74 0 50 clear
08/01/2022 3 390 73 5 60 overcast 71 2 70 cloudy

The report has a working query, which is shown below. Would it be possible to refactor this somehow using a pivot or some other method so that I don't have to individually select every piece of data for every station?

select a.reading_date,
    a.hour_value,
    a.telemetry_value_1,

(select AVG(temperature) from WEATHER_ACTUALS b where station_id = 'station1' and reading_date = a.reading_date and hour_value = a.hour_value) as c1_temp,
(select AVG(wind) from WEATHER_ACTUALS b where station_id = 'station1' and reading_date = a.reading_date and hour_value = a.hour_value) as c1_wind,
(select AVG(humidity) from WEATHER_ACTUALS b where station_id = 'station1' and reading_date = a.reading_date and hour_value = a.hour_value) as c1_humidity,
(select conditions from WEATHER_ACTUALS b where rownum = 1 and station_id = 'station1' and reading_date = a.reading_date and hour_value = a.hour_value) as c1_conditions,

(select AVG(temperature) from WEATHER_ACTUALS b where station_id = 'station2' and reading_date = a.reading_date and hour_value = a.hour_value) as c2_temp,
(select AVG(wind) from WEATHER_ACTUALS b where station_id = 'station2' and reading_date = a.reading_date and hour_value = a.hour_value) as c2_wind,
(select AVG(humidity) from WEATHER_ACTUALS b where station_id = 'station2' and reading_date = a.reading_date and hour_value = a.hour_value) as c2_humidity,
(select conditions from WEATHER_ACTUALS b where rownum = 1 and station_id = 'station2' and reading_date = a.reading_date and hour_value = a.hour_value) as c2_conditions,

...

(select AVG(temperature) from WEATHER_ACTUALS b where station_id = 'station10' and reading_date = a.reading_date and hour_value = a.hour_value) as c10_temp,
(select AVG(wind) from WEATHER_ACTUALS b where station_id = 'station10' and reading_date = a.reading_date and hour_value = a.hour_value) as c10_wind,
(select AVG(humidity) from WEATHER_ACTUALS b where station_id = 'station10' and reading_date = a.reading_date and hour_value = a.hour_value) as c10_humidity,
(select conditions from WEATHER_ACTUALS b where rownum = 1 and station_id = 'station10' and reading_date = a.reading_date and hour_value = a.hour_value) as c10_conditions

  from telemetry_data a
 where a.reading_date between :beginDate and :endDate

CodePudding user response:

PIVOT the stations from rows to columns:

SELECT t.*,
       w.c1_temp,
       w.c1_hum,
       w.c1_wind,
       w.c1_cond,
       w.c2_temp,
       w.c2_hum,
       w.c2_wind,
       w.c2_cond,
       w.c3_temp,
       w.c3_hum,
       w.c3_wind,
       w.c3_cond
FROM   telemetry t
       INNER JOIN (
         SELECT *
         FROM   weather
         PIVOT (
           AVG(temperature) AS temp,
           AVG(humidity) AS hum,
           AVG(wind) AS wind,
           MAX(conditions) KEEP (DENSE_RANK FIRST ORDER BY ROWNUM) AS cond
           FOR station_id IN (
             'station1' AS c1,
             'station2' AS c2,
             'station3' AS c3
           )
         )
       ) w
       ON (   t.reading_date = w.reading_date
          AND t.hour_value   = w.hour_value)

Which, for the sample data:

CREATE TABLE weather (READING_DATE, HOUR_VALUE, STATION_ID, TEMPERATURE, HUMIDITY, WIND, CONDITIONS) AS
SELECT DATE '2022-01-08', 1, 'station1', 77,  50, 1, 'clear'    FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 1, 'station2', 75,  50, 0, 'clear'    FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 1, 'station3', 74,  60, 3, 'overcast' FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 2, 'station1', 76,  50, 3, 'clear'    FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 2, 'station2', 74,  50, 0, 'clear'    FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 2, 'station3', 70,  65, 2, 'overcast' FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 3, 'station1', 73,  60, 5, 'cloudy'   FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 3, 'station2', 71,  70, 2, 'cloudy'   FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 3, 'station3', 69, 100, 3, 'rain'     FROM DUAL;

CREATE TABLE telemetry (READING_DATE, HOUR_VALUE, TELEMETRY_VALUE_1, TELEMETRY_VALUE_2) AS
SELECT DATE '2022-01-08', 1, 430, 10 FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 2, 405,  9 FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 3, 390,  8 FROM DUAL;

Outputs:

READING_DATE HOUR_VALUE TELEMETRY_VALUE_1 TELEMETRY_VALUE_2 C1_TEMP C1_HUM C1_WIND C1_COND C2_TEMP C2_HUM C2_WIND C2_COND C3_TEMP C3_HUM C3_WIND C3_COND
08-JAN-22 1 430 10 77 50 1 clear 75 50 0 clear 74 60 3 overcast
08-JAN-22 2 405 9 76 50 3 clear 74 50 0 clear 70 65 2 overcast
08-JAN-22 3 390 8 73 60 5 cloudy 71 70 2 cloudy 69 100 3 rain

fiddle

  • Related