我正在Oracle中查询一个包含数十亿行(BIG_TABLE
)的表。该表按月更新(DTE
是流量的月份)。DBA希望我们按流量的月份过滤查询(我不是在质疑这一点;实际上:(i)不这样做会导致性能急剧下降,以及(ii)DBA警告他可能会杀死未过滤的查询)。
因为我需要几个月(有时多达100),我使用参数化查询来传递我需要的月份(更多信息见下文)。我在中间表(INTERMEDIATE_TABLE
,每月包含约2亿行)中收集所有月份的结果,然后聚合数据(FINAL_TABLE
,用于分析)。无论流量的月份如何,总和必须由CHR
完成。
-- query1
CREATE TABLE
INTERMEDIATE_TABLE (
CHR VARCHAR2(255),
NBR NUMBER,
DTE DATE
);
-- query2
INSERT INTO
INTERMEDIATE_TABLE
SELECT
CHR,
NBR,
DTE
FROM
BIG_TABLE
WHERE
DTE = TO_DATE(?, 'YYYY-MM-DD');
-- query3
CREATE TABLE
FINAL_TABLE AS
SELECT
CHR,
SUM(NBR) AS NBR
FROM
INTERMEDIATE_TABLE
GROUP BY
CHR;
将查询保存为字符串(query1
、query2
、query3
)后,我使用R的DBI运行查询。dbExecute()
将日期逐个传递给query2
中的相等比较,因此查询执行了3次(即列表不是一次传递的)。
library(DBI)
dbConnect(odbc::odbc(), ...)
dbExecute(con, query1)
dbExecute(con, query2, params = list(c("2020-01-01", "2020-02-01", "2020-03-01")))
dbExecute(con, query3)
我希望INTERMEDIATE_TABLE
是临时的。我可以INSERT INTO
CTE吗?
我对其他解决方案持开放态度,但我正在寻找ANSI解决方案,因为计划迁移到PostgreSQL。我想避免临时表,因为Oracle中的only the data is temporary,而不是表。当然,我可以在之后只DROP
表,但首先使其临时似乎更优雅/明确(和有效?)。
一个可重复的例子:
CREATE TABLE
BIG_TABLE (
CHR VARCHAR2(255),
NBR NUMBER,
DTE DATE
);
INSERT ALL
INTO BIG_TABLE VALUES ('A', 2, DATE '2020-01-01')
INTO BIG_TABLE VALUES ('B', 3, DATE '2020-01-01')
INTO BIG_TABLE VALUES ('A', 1, DATE '2020-02-01')
INTO BIG_TABLE VALUES ('B', 2, DATE '2020-02-01')
INTO BIG_TABLE VALUES ('A', 3, DATE '2020-02-01')
INTO BIG_TABLE VALUES ('B', 2, DATE '2020-03-01')
INTO BIG_TABLE VALUES ('B', 4, DATE '2020-03-01')
INTO BIG_TABLE VALUES ('C', 1, DATE '2020-03-01')
INTO BIG_TABLE VALUES ('B', 4, DATE '2020-04-01')
INTO BIG_TABLE VALUES ('D', 2, DATE '2020-05-01')
SELECT 1 FROM DUAL;
预期输出:
CHR NBR
A 6
B 11
C 1
3条答案
按热度按时间5lwkijsr1#
您根本不需要任何类型的中间阶段,甚至不需要子查询。
当然,对于10亿行,你需要并行性,这取决于表的配置方式。显式地请求它可能会有所帮助:
如果你需要多个月,你有几个选择。我建议一个单一的查询,以最大化分区的并行线程分布的方式,如果这是一个分区表:
如果你需要拉取多个月,并让它们都在这个表中结束,而一次只拉取一个月,那么预先创建这个表并插入每个月:
然后在最后,如果你需要整个时间的聚合,你可以做一个group by来排除日期:
1tu0hz3e2#
您可以使用子查询来获取中间表并将其作为中间表使用,这样所有中间表都将在一个查询中运行。
作为CTE
bxgwgixi3#
考虑使用物化视图(MV)而不是临时表来执行此操作。这具有相同的效果-您正在预计算并将结果存储在表中-但具有更多的内置控件来帮助您刷新数据。
例如,创建以下MV:
这将存储一行/chr/month。除非每个月有数百万个'chr'值,否则这应该足以避免“按月过滤”的要求,因为每次阅读的数据将大大减少。
如果您将MV设置为
fast refresh on commit
,则数据库将自动使其保持最新。即使你不得不使用“按月过滤”的方法,你仍然可以将其构建到MV中。
创建一个过滤表来存储所需的月份,在每次运行前插入值,并刷新MV: