Leo*_*nto 9 sql postgresql aggregate distinct postgresql-performance
我在表中有350万行acs_objects,我需要检索creation_date具有年份格式和不同的列.
我的第一次尝试:180~200 Sec (15 Rows Fetched)
SELECT DISTINCT to_char(creation_date,'YYYY') FROM acs_objects
Run Code Online (Sandbox Code Playgroud)
我的第二次尝试:35~40 Sec (15 Rows Fetched)
SELECT DISTINCT to_char(creation_date,'YYYY')
FROM (SELECT DISTINCT creation_date FROM acs_objects) AS distinct_date
Run Code Online (Sandbox Code Playgroud)
有没有办法让它更快? - "我需要在ADP网站上使用它"
val*_*lex 14
我想你不应该distinct从这张巨大的桌子中选择.而是尝试生成短年序列(例如从1900年到2100年),并从该序列中选择仅存在于acs_objects表中的年份.结果集将是相同的,但我认为它会更快.EXISTS子查询必须在索引字段上快速运行creation_date.
SELECT y
FROM
(
select generate_series(1900,2100) as y
) as t
WHERE EXISTS (SELECT 1 FROM acs_objects
WHERE creation_date >= DATE (''||t.y||'-01-01')
AND creation_date < DATE (''||t.y + 1||'-01-01'))
Run Code Online (Sandbox Code Playgroud)
在第二次尝试中,您将从子查询中获取不同的日期,然后您将所有日期转换为字符串表示,然后选择不同的日期.这是相当低效的.最好先从creation_date子查询中提取不同的年份,然后将它们转换为主查询中的文本:
SELECT year::text
FROM (
SELECT DISTINCT extract(year FROM creation_date) AS year FROM acs_objects
) AS distinct_years;
Run Code Online (Sandbox Code Playgroud)
如果INDEX在表上创建一个,查询应该运行得更快:
CREATE INDEX really_fast ON acs_objects((extract(year FROM creation_date)));
Run Code Online (Sandbox Code Playgroud)
但是,这可能会影响表的其他用途,特别是如果您有许多修改语句(插入,更新,删除).这只有creation_date在数据类型为date或timestamp(特别是不是timestamp with timezone)的情况下才有效.
下面的选项看起来很有前景,因为它不使用子查询,但事实上它要慢得多(参见下面的注释),可能是因为该DISTINCT子句应用于字符串:
SELECT DISTINCT extract(year FROM creation_date)::text
FROM acs_objects;
Run Code Online (Sandbox Code Playgroud)
我不确定你用它做什么。我可能会考虑使用使用物化视图。
现在,您可以在需要时刷新视图,并有一种非常快速的方法来检索(不同的)年份列表(因为数据基本上是静态存储的)。
看看这里:
有什么办法可以让它更快吗?
哦,是的,快得多。(2021 年更新。)
如果您经常且快速地需要此操作,并且对表的写入很少或可预测(例如:新行始终具有当前时间),那么物化视图将是最快的,就像@Roger建议的那样。但您仍然需要一个查询来实现它。我要建议的查询太快了,你可能会跳过 MV ...
在相关情况下,通常会有一个包含候选值的查找表,可以实现更快的查询:
本案例的假设:
creation_date是数据类型timestamp(也适用于date或timestamptz)。acs_objects(creation_date)。如果您既没有查找表也没有包含候选值的派生表,那么仍然有一个非常快速的替代方案。基本上,您需要模拟“索引跳过扫描”,也称为“松散索引扫描”。此查询在任何情况下都有效:
WITH RECURSIVE cte AS (
SELECT date_trunc('year', max(creation_date)) AS y
FROM acs_objects
UNION ALL
SELECT (SELECT date_trunc('year', max(creation_date))
FROM acs_objects
WHERE creation_date < cte.y)
FROM cte
WHERE cte.y IS NOT NULL
)
SELECT to_char(y, 'YYYY') AS year
FROM cte
WHERE cte.y IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)
可能最快:自上而下,将每个时间戳截断到年初,然后找到较早行中的最新行;重复。
该技术的详细信息:
generate_series()valex 的想法可以通过根据现有年份的实际范围generate_series()生成timestamp值来更有效地实现:
SELECT to_char(y, 'YYYY') AS year
FROM (
SELECT generate_series(date_trunc('year', min(creation_date))
, max(creation_date)
, interval '1 year')
FROM acs_objects
) t(y)
WHERE EXISTS (
SELECT FROM acs_objects
WHERE creation_date >= y
AND creation_date < y + interval '1 year'
);
Run Code Online (Sandbox Code Playgroud)
如果年份范围内的差距很小,那么这可能会更快。但无论表大小如何,两者都只需要几毫秒或更短的时间。
有关的:
| 归档时间: |
|
| 查看次数: |
1508 次 |
| 最近记录: |