Компилирование двух запросов похожих данных в одно представление с SQL - PullRequest
0 голосов
/ 08 апреля 2019

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

Структура

Таблица1 содержит информацию о том, когда пользователи входят в определенную систему. Таблица 2 содержит информацию о том, когда пользователи входят в другую систему Таблица3 содержит информацию, специфичную для пользователя

Запрос - Построить 1 запрос, который показывает в одной таблице точную временную шкалу входа пользователя в обе системы.

Вопросы -

  1. Пользователи могут запустить задачу в одной системе и не завершить ее, оставив ее в состоянии ожидания. Из-за этого в этой системе нет конечной отметки времени. Я хотел бы найти следующую хронологическую метку времени и использовать эту -1 секунду в качестве конечных значений для «отложенной» задачи. (Пользователи могут никогда не выполнить указанное задание)

  2. Я создал комбинированное представление, используя объединение, однако это кажется неэффективным, и с ним я не уверен, как решить проблему 1. Поэтому я пытаюсь найти способ построить 1 не объединение запрос, который объединяет значения в соответствующие столбцы и оттуда применяет любую дополнительную логику для определения end_ts, чтобы не было нулевых значений.


select 

a.uid as "user's ID",
a.first_name as "First Name",
a.last_name as "Last Name",


e.task_started_ts as "start_gmt_ts", //<-- I'd like to use the next val if previous end was null
e.task_ended_ts as "end_gmt_ts", //<-- This ts may be null
e.task_desc_cd as "task",

(extract(day from (e.task_ended_ts - e.task_started_ts DAY(4) TO SECOND)) * 86400) +
(extract(hour from (e.task_ended_ts - e.task_started_ts DAY(4) TO SECOND)) * 3600) +
(extract(minute from (e.task_ended_ts - e.task_started_ts DAY(4) TO SECOND)) * 60) +
extract(second from (e.task_ended_ts - e.task_started_ts DAY(4) TO SECOND))
as duration_secs,

e.task_created_ts as "created_at",
e.task_created_date as "reporting date"

from foo.Table1 e

JOIN baz.Table3 a ON a.users_db_id = e.users_db_id 
    and date BETWEEN a.role_start AND a.role_end

where 1=1

and e.users_db_id is in ('42')
and e.task_created_date > '2019-03-15'

UNION

SELECT

  e.uid as "user's ID",
  e.first_name as "First Name",
  e.last_name as "Last Name",
  c.task_started_ts as "start_gmt_ts", //<-- I'd like to use the next val if previous end was null
  c.task_ended_ts as "end_gmt_ts",
  case 
       when task_desc_code = '0' then 'task0'
       when task_desc_code = '1' then 'task1'
       when task_desc_code = '2' then 'task2'
       when task_desc_code = '3' then 'task3'
       when task_desc_code = '4' then 'task4'
       when task_desc_code = '5' then 'task5'       
       when task_desc_code = '6' then 'task6'
       when task_desc_code = '7' then 'task7'
       when task_desc_code = '8' then 'task8'
       when task_desc_code = '9' then 'task9'
       when task_desc_code = '10' then 'task10'
       else task_desc_code end as task,

  case when total_time_seconds = 0 then
    (extract(day from (c.task_ended_ts - c.task_started_ts DAY(4) TO SECOND)) * 86400) +
    (extract(hour from (c.task_ended_ts - c.task_started_ts DAY(4) TO SECOND)) * 3600) +
    (extract(minute from (c.task_ended_ts - c.task_started_ts DAY(4) TO SECOND)) * 60) +
    extract(second from (c.task_ended_ts - c.task_started_ts DAY(4) TO SECOND))
  else total_time_seconds end as duration_secs,

  c.task_created_ts as "created_at",
  c.task_created_date as "reporting date"

FROM bar.Table2 c
JOIN baz.Table3 e ON e.users_db_id = c.users_db_id 
  AND date BETWEEN e.role_start AND e.role_end
  WHERE c.task_created_date >= '2019-03-15' 
  AND e.uid in ('42')

Как видно из приведенного выше, каждый запрос создает одинаковые столбцы с одинаковыми значениями. Я бы предпочел не использовать Union и найти способ вытащить данные из двух таблиц и объединить их, основываясь на хронологическом порядке начальных временных отметок. Затем, когда я пробегаю нулевую отметку времени окончания, я могу вычесть 1 секунду из отметки времени начала следующего времени и использовать ее в качестве отметки времени окончания.

Не уверен, если это возможно или куда идти отсюда. Любая помощь приветствуется

1 Ответ

0 голосов
/ 08 апреля 2019

Итак, во-первых, UNION ALL записи из Table1 и Table2, а затем выполняйте вычисления и соединяйте их в другом месте, что-то вроде:

SELECT usrs.uid as "user's ID"
     , usrs.first_name as "First Name"
     , usrs.last_name as "Last Name"
     , both_systems.task_started_ts as "start_gmt_ts" //<-- I'd like to use the next val if previous end was null
     , both_systems.task_ended_ts as "end_gmt_ts" //<-- This ts may be null
     , both_systems.task_desc_cd as "task"

     , (extract(day from (both_systems.task_ended_ts - both_systems.task_started_ts DAY(4) TO SECOND)) * 86400) +
       (extract(hour from (both_systems.task_ended_ts - both_systems.task_started_ts DAY(4) TO SECOND)) * 3600) +
       (extract(minute from (both_systems.task_ended_ts - both_systems.task_started_ts DAY(4) TO SECOND)) * 60) +
       extract(second from (both_systems.task_ended_ts - both_systems.task_started_ts DAY(4) TO SECOND))
     as duration_secs

     , both_systems.task_created_ts as "created_at",
     , both_systems.task_created_date as "reporting date"
  FROM (-- Select as many columns as you need, just make sure columns are the same between both tables
       SELECT '1' AS system_in_use, c.task_started_ts, c.task_ended_ts, c.task_desc_code, c.users_db_id , c.total_time_seconds, c.task_created_ts, c.task_created_date, c.date
         FROM foo.Table1 c
        UNION ALL
       SELECT '2' AS system_in_use, c.task_started_ts, c.task_ended_ts, c.task_desc_code, c.users_db_id , c.total_time_seconds, c.task_created_ts, c.task_created_date, c.date
         FROM bar.Table2 c) both_systems
  JOIN baz.Table3 usrs
    ON usrs.users_db_id = c.both_systems
    -- not sure where date column is coming from, I presume task
    -- hint: never use non-qualified column names in multi-table queries
   AND both_systems.date BETWEEN usrs.role_start AND usrs.role_end
 WHERE both_systems.task_created_date >= '2019-03-15' 
   AND usrs.uid in ('42')

Теперь об использовании даты начала следующей задачив качестве даты окончания в случае, если дата окончания текущей задачи равна NULL, используйте LEAD.Что-то вроде:

COALESCE(both_systems.task_ended_ts, LEAD(both_systems.task_started_ts IGNORE NULLS, 1, 0) OVER (PARTITION BY usrs.uid ORDER BY both_systems.task_started_ts)) AS task_ended_ts

Итак, сначала возьмите tasks_ended_ts и только если это NULL, найдите следующую строку, используя LEAD.

Возможно, вам понадобится другой подзапрос, еслиВы также хотите рассчитать duration_secs по этой формуле.

...