Как динамически получать значения лет в Oracle / SQL в зависимости от того, доступны они (не нулевые) или нет (нулевые) - PullRequest
0 голосов
/ 07 ноября 2019

Допустим, у меня есть таблица, в которой есть человек с весом в год.

 NAME     2012        2013       2014         2015        2016         2017      2018         2019
JOHN      180          185       192          205         199          198       193          null
MIKE      190          191       191          195         195          195       195          195

То, что я пытаюсь сделать, это захватить мин / макс за последние NULL лет. (как показано в наборе результатов ниже)

NAME     MIN         MAX
JOHN     192         205
MIKE     195         195 

Ниже, что я пробовал, есть ли лучший способ сделать это?

SELECT 
    CASE WHEN 2019 IS NULL
         AND 2018 > 2017 > 2016 > 2015 > 2014
         THEN 2018
         WHEN 2019 IS NULL
         AND 2017 > 2016 > 2015 > 2014 > 2018
         THEN 2017
         WHEN 2019 IS null
         AND 2016 > 2017 > 2018 > 2015 > 2014
         THEN 2016
         WHEN 2019 IS NULL
         AND 2015 > 2018 > 2017 > 2016 > 2014
         THEN 2015
         WHEN 2019 IS NULL
         AND 2014 > 2018 > 2017 > 2016 > 2015
         THEN 2014
         WHEN 2019 IS NOT NULL
         AND 2019 > 2018 > 2017 > 2016 > 2015 > 2014
         THEN 2019
         WHEN 2019 IS NOT NULL
         AND 2018 > 2017 > 2016 > 2015 > 2014 > 2018
         THEN 2018
         WHEN 2019 IS NOT null
         AND 2017 > 2017 > 2018 > 2015 > 2014 > 2019
         THEN 2017
         WHEN 2019 IS NOT NULL
         AND 2016> 2015 > 2018 > 2017 > 2019 > 2014
         THEN 2016
         WHEN 2019 IS NOT NULL
         AND 2015 > 2014 > 2018 > 2017 > 2016 > 2019
         THEN 2015
         WHEN 2019 IS NOT NULL
         AND 2014 > 2015 > 2015 > 2018 > 2017 > 2016 > 2019
         THEN 2014
          END                                  as MAX

Заранее спасибо ОТ таблицы ГДЕ

Ответы [ 3 ]

2 голосов
/ 07 ноября 2019

Функции Greatest () и Least () помогут в этом случае

Функция Greatest () сравнивает столбцы и обеспечивает максимальное значение из этих столбцов, а функция Least () обеспечивает минимальное значение из этих столбцов.

     select name,
             LEAST(2012,2013,2014,2015,2016,2017,2018) as MIN_WEIGHT
            , GREATEST(2012,2013,2014,2015,2016,2017,2018) as MAX_WEIGHT
     from your_table
     /
2 голосов
/ 07 ноября 2019

Ваш дизайн таблицы не оптимален, и было бы лучше нормализовать его, получая каждый вес в год на отдельной записи вместо столбца. Тем не менее, если вы должны продолжить, вы можете использовать один трюк - COALESCE, чтобы заменить NULL вес столбца -1. Это эффективно вычеркнет это из картины. Затем используйте скалярную функцию GREATEST, чтобы найти допустимый максимум:

SELECT
    GREATEST(COALESCE("2019", -1),
             COALESCE("2018", -1),
             COALESCE("2017", -1),
             COALESCE("2016", -1),
             COALESCE("2015", -1),
             COALESCE("2014", -1),
             COALESCE("2013", -1),
             COALESCE("2012", -1)) AS max_weight
FROM yourTable;

Примечание: если вероятность того, что каждый столбец будет равен NULL, тогда приведенный выше вызовGREATEST вернет -1. В этом случае вы можете либо оставить это значение в качестве рынка для недействительных данных, либо заключить в выражение CASE и заменить его другим значением.

1 голос
/ 07 ноября 2019

Что касается вашего замечания по поводу ответа Тима

, если 2019 равен нулю, мы хотим рассчитать максимум только с 2018-2013. если 2019 доступен только 2019-2014

, вы хотите мин / макс в течение 6 лет.

Самый простой способ - сначала нормализовать данные, используя UNPIVOT, по умолчанию это удаляет NULL:

SELECT NAME, weight, yr,
   Row_Number() Over (PARTITION BY NAME ORDER BY yr DESC) AS rn
FROM mytable
UNPIVOT(weight
    FOR yr
    IN (
        y2012 as 2012
       ,y2013 as 2013
       ,y2014 as 2014
       ,y2015 as 2015
       ,y2016 as 2016
       ,y2017 as 2017
       ,y2018 as 2018
       ,y2019 as 2019
    )
)

Затем вы применяете свою логику с помощью оконных агрегатов, чтобы получить только последние 6 лет. ,Используя row_number, вы получите последние 6 строк

WITH cte AS 
 (
    SELECT NAME, weight, yr,
       Row_Number() Over (PARTITION BY NAME ORDER BY yr DESC) AS rn
    FROM mytable
    UNPIVOT(weight
        FOR yr
        IN (
            y2012 as 2012
           ,y2013 as 2013
           ,y2014 as 2014
           ,y2015 as 2015
           ,y2016 as 2016
           ,y2017 as 2017
           ,y2018 as 2018
           ,y2019 as 2019
        )
    )
 )
SELECT NAME, Min(weight), Max(weight), Min(yr), Max(yr)
FROM cte
WHERE rn BETWEEN 1 AND 6
GROUP BY NAME

Используя max, вы получите строки за последние 6 лет (которые будут возвращать другоерезультат, если есть дополнительные NULL):

WITH cte AS 
 (
   SELECT NAME, weight, yr, 
      Max(yr) Over (PARTITION BY NAME) AS maxyr
    FROM mytable
    UNPIVOT(weight
        FOR yr
        IN (
            y2012 as 2012
           ,y2013 as 2013
           ,y2014 as 2014
           ,y2015 as 2015
           ,y2016 as 2016
           ,y2017 as 2017
           ,y2018 as 2018
           ,y2019 as 2019
        )
    )
 )
SELECT NAME, Min(weight), Max(weight), Min(yr), Max(yr)
FROM cte
WHERE yr BETWEEN maxyr -5 AND maxyr
GROUP BY NAME

Смотрите dbfiddle

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...