Агрегирующая функция с аналитической функцией над разделением на неагрегированных значениях, выбранных терадатой, должна быть частью связанной группы - PullRequest
0 голосов
/ 04 января 2019

Я должен посчитать количество изменений статуса, но только если разница во времени от одного статуса к другому составляет менее 30 минут.В моей базе данных у меня есть текущее время и предыдущий столбец времени, который я сделал с помощью перегородки.Вот мой запрос, но я получаю сообщение об ошибке: «Выбранные неагрегированные значения должны быть частью связанной группы».Может ли кто-нибудь помочь?

select col1, col2,
    MAX(creation_dt_utc) OVER(PARTITION BY col1,col2,col3 ORDER BY creation_dt ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS previous_creation_dt,
    (creation_dt - prev_creation_dt) DAY(4) TO SECOND(6) as time_difference,
    EXTRACT(DAY FROM time_difference) * 24*60 + EXTRACT(HOUR FROM time_difference) * 60 + EXTRACT(MINUTE FROM time_difference) AS Total_Minutes
    SUM(
        CASE WHEN status_previous='Test1'
                and status_current='Test2' THEN 1

            ELSE    

                CASE WHEN status_previous='Test3'
                    and status_current='Test2' THEN 1                           
            ELSE
                CASE WHEN status_previous='Test4'
                    and status_current='Test2' THEN 1                       
                ELSE 0
                END
            END
        END
    ) AS "Total_Change"
from myTable
qualify Total_Minutes<30
where EXTRACT(YEAR from year_column)='2017';

enter image description here

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Аналитические функции обрабатываются после агрегирования (где-из-группы путем-olap-qualify-order by), поэтому вы не можете применить агрегирование к результату OVER, вы должны вложить его в производную таблицуили общее табличное выражение:

SELECT
   Sum(
       CASE WHEN (status_previous='Test1' AND status_current='Test2')
              OR (status_previous='Test3' AND status_current='Test2')
              OR (status_previous='Test4' AND status_current='Test2')
            THEN 1                       
            ELSE 0
       END) AS "Total_Change"
FROM
 (
   SELECT col1, col2,
       Max(creation_dt_utc)
       Over(PARTITION BY col1,col2,col3
            ORDER BY creation_dt
            ROWS BETWEEN 1 Preceding AND 1 Preceding) AS previous_creation_dt,

       (creation_dt - prev_creation_dt) DAY(4) TO SECOND(6) AS time_difference,

       Extract(DAY From time_difference) * 24*60 + Extract(HOUR From time_difference) * 60 + Extract(MINUTE From time_difference) AS Total_Minutes

   FROM myTable
   WHERE Extract(YEAR From year_column)=2017 -- the result of EXTRACT is an INTEGER, not a string
   QUALIFY Total_Minutes<30
 ) AS dt

Но так как вам нужен только счет, вы можете переместить CASE в QUALIFY:

SELECT Count(*) AS "Total_Change"
FROM
 (
   SELECT col1, col2,
       Max(creation_dt_utc)
       Over(PARTITION BY col1,col2,col3
            ORDER BY creation_dt
            ROWS BETWEEN 1 Preceding AND 1 Preceding) AS previous_creation_dt,

       (creation_dt - prev_creation_dt) DAY(4) TO SECOND(6) AS time_difference,

       Extract(DAY From time_difference) * 24*60 + Extract(HOUR From time_difference) * 60 + Extract(MINUTE From time_difference) AS Total_Minutes

   FROM myTable
   WHERE Extract(YEAR From year_column)=2017 -- the result of EXTRACT is an INTEGER, not a string
   QUALIFY Total_Minutes<30
       AND (   (status_previous='Test1' AND status_current='Test2')
            OR (status_previous='Test3' AND status_current='Test2')
            OR (status_previous='Test4' AND status_current='Test2')
           )
 ) AS dt

Edit:

Логика CASEможет быть дополнительно упрощено до:

CASE WHEN status_current='Test2' and status_previous IN ('Test1','Test3','Test4')
     THEN 1                       
     ELSE 0
END

или, возможно,

CASE WHEN status_current='Test2' and status_previous <>'Test2'
     THEN 1                       
     ELSE 0
END
0 голосов
/ 04 января 2019

Я думаю, что QUALIFY должен быть после предложения WHERE.

А для предыдущего значения я предполагаю, что LAG будет более подходящим, чем MAX.

А те вложенные CASE могутбыть записано как 1 CASE.Поскольку после выполнения условия WHEN другие условия WHEN после него не проверяются.

Так как используется обычный SUM, должен быть GROUP BY.

SELECT col1, col2,
 COUNT(*) AS Total,
 SUM(TimeDiffMinutes) AS Total_Minutes,
 SUM(CASE WHEN StatusChanged = 1 THEN TimeDiffMinutes ELSE 0 END) AS Total_Minutes_Change,
 COUNT(CASE WHEN StatusChanged = 1 THEN 1 END) AS Total_Change
FROM
(
  SELECT col1, col2, col3, creation_dt,
  (CASE 
   WHEN status_previous='Test1' and status_current='Test2' THEN 1
   WHEN status_previous='Test3' and status_current='Test2' THEN 1   
   WHEN status_previous='Test4' and status_current='Test2' THEN 1
   ELSE 0
   END) AS StatusChanged,
  LAG(creation_dt) OVER (PARTITION BY col1, col2, col3 ORDER BY creation_dt) AS prev_creation_dt,
  (creation_dt - prev_creation_dt) DAY(4) TO SECOND(6) AS time_difference,
  EXTRACT(DAY FROM time_difference)*(24*60) + EXTRACT(HOUR FROM time_difference)*60 + EXTRACT(MINUTE FROM time_difference) AS TimeDiffMinutes
  FROM myTable  
  WHERE EXTRACT(YEAR from year_column) = '2017'
  QUALIFY (creation_dt - prev_creation_dt) day(4) to second(6) < interval '30' minute
) q
GROUP BY col1, col2
ORDER BY col1, col2
...