我有一个进行一些计算的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或其他重构技术使此查询更易于理解吗?谢谢!
我将使用子查询来避免重复,减少连接数并使用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;
此查询可以重构,但是它变得“更易于阅读” ...:
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;