Как я могу сделать этот запрос более читабельным? - PullRequest
0 голосов
/ 26 февраля 2020

У меня есть запрос SQL, который выполняет некоторые вычисления. Помимо простых изменений форматирования, мне интересно, возможно ли каким-то образом использовать CTE, чтобы этот запрос было легче читать? Я обнаружил несколько возможностей, когда использование CTE может существенно повлиять на понятность запроса, но выполнение этого запроса ускользает от меня.

Схема:

CREATE TABLE public.items (
    id bigint NOT NULL,
    uuid uuid NOT NULL,
);

CREATE TABLE public.download_counts (
    item_id uuid NOT NULL,
    date date NOT NULL,
    download_count integer NOT NULL,
);

CREATE TABLE public.view_counts (
    item_id uuid NOT NULL,
    date date NOT NULL,
    view_count integer NOT NULL,
);

Запрос:

select
  i.uuid as item_id,
  (
    (
      sum(dlw.download_count) * (
        case
        when sum(vlw.view_count) = 0 then sum(dlw.download_count)
        else sum(vlw.view_count)
        end
      )
    ) - (
      sum(dpw.download_count) * (
        case
        when sum(vpw.view_count) = 0 then sum(dpw.download_count)
        else sum(vpw.view_count)
        end
      )
    )
  ) * 100 / (
    sum(dpw.download_count) * (
      sum(dpw.download_count) * (
        case
        when sum(vpw.view_count) = 0 then sum(dpw.download_count)
        else sum(vpw.view_count)
        end
      )
    )
  ) as trending_score
from
  items as i
left join download_counts as dlw
  on dlw.item_id = i.uuid
  and dlw.date between (now()::date - interval '1 week') and (now()::date - interval '1 day')
left join download_counts as dpw
  on dpw.item_id = i.uuid
  and dpw.date between (now()::date - interval '2 week') and (now()::date - interval '8 days')
left join view_counts as vlw
  on vlw.item_id = i.uuid
  and vlw.date between (now()::date - interval '1 week') and (now()::date - interval '1 day')
left join view_counts as vpw
  on vpw.item_id = i.uuid
  and vpw.date between (now()::date - interval '2 week') and (now()::date - interval '8 days')
where dlw.item_id is not null or dpw.item_id is not null or vlw.item_id is not null or vpw.item_id is not null
group by i.uuid;

Могу ли я использовать CTE или какую-либо другую технику рефакторинга для облегчения понимания этого запроса? Спасибо!

Ответы [ 2 ]

0 голосов
/ 26 февраля 2020

Этот запрос может быть реорганизован, но становится ли он «проще для чтения» ...:

select item_id
,( sumdlwdc * case when sumvlwvc = 0 then sumdlwdc else 0 end 
 - sumdpwdc * case when sumvpwvc = 0 then sumdpwdc else 0 end )
* 100 / ( sumdpwdc * (sumdpwdc * case when sumvpwvc = 0 then sumdpwdc else 0 end)) as trending_score
from(
    select item_id,
    sum(case when doffset = 0 then download_count else 0 end ) sumdlwdc,
    sum(case when voffset = 0 then view_count else 0 end ) sumvlwvc,
    sum(case when doffset = -1 then download_count else 0 end ) sumdpwdc,
    sum(case when voffset = -1 then view_count else 0 end ) sumvpwvc
    from (
        select
          i.uuid as item_id,
          dlpw.download_count, vlpw.view_count,
          case when dlpw.date between (now()::date - interval '1 week') and (now()::date - interval '1 day') then 0 else -1 end doffset,
          case when vlpw.date between (now()::date - interval '1 week') and (now()::date - interval '1 day') then 0 else -1 end voffset
        from
          items as i
        left join download_counts as dlpw
          on dlpw.item_id = i.uuid
          and dlpw.date between (now()::date - interval '2 week') and (now()::date - interval '1 day')
        left join view_counts as vlpw
          on vlpw.item_id = i.uuid
          and vlpw.date between (now()::date - interval '2 week') and (now()::date - interval '1 day')
        where dlpw.item_id is not null or vlpw.item_id is not null
    )q
    group by item_id
)q1;
0 голосов
/ 26 февраля 2020

Я бы использовал подзапрос, чтобы избежать повторения, уменьшить количество объединений и выборочно агрегировать, используя предложение FILTER. Последнее также хорошо для производительности.

И я хотел бы использовать SQL ключевые слова с заглавной буквы.

SELECT i.uuid as item_id,
       (
          dlw * (CASE WHEN vlw = 0 THEN dlw ELSE vlw END)
        - dpw * (CASE WHEN vpw = 0 THEN dpw ELSE vpw END)
       )
       * 100.0
       / dpw
       / dpw
       / (CASE WHEN vpw = 0 THEN dpw ELSE vpw END)
       as trending_score
FROM (SELECT coalesce(sum(d.download_count), 0)
                FILTER (WHERE d.date BETWEEN current_date - 7
                                         AND current_date - 1)
                AS dlw,
             coalesce(sum(d.download_count), 0)
                FILTER (WHERE d.date BETWEEN current_date - 14
                                         AND current_date - 8)
                AS dpw,
             coalesce(sum(v.view_count), 0)
                FILTER (WHERE v.date BETWEEN current_date - 7
                                         AND current_date - 1)
                AS vlw,
             coalesce(sum(v.view_count), 0)
                FILTER (WHERE v.date BETWEEN current_date - 14
                                         AND current_date - 8)
                AS vpw,
      FROM items as i
         LEFT JOIN download_counts AS d
            ON d.item_id = i.uuid
               AND d.date BETWEEN current_date - 14
                              AND current_date - 1
         LEFT JOIN view_counts AS v
            ON v.item_id = i.uuid
               AND v.date BETWEEN current_date - 14
                              AND current_date - 1
      GROUP BY i.uuid) AS subq;
...