并集的条件偏移 select
Conditional offset on union select
假设我们有来自数十个具有不同结构但相似字段含义的表的复杂联合 select:
SELECT a1.abc as field1,
a1.bcd as field2,
a1.date as order_date,
FROM a1_table a1
UNION ALL
SELECT a2.def as field1,
a2.fff as field2,
a2.ts as order_date,
FROM a2_table a2
UNION ALL ...
ORDER BY order_date
另请注意,结果通常按 "synthetic" 字段 order_date
.
排序
此查询提供了大量行,我们希望处理来自这组行的页面。每个页面由两个参数定义:
- 页面大小
field2
上一页最后一项的值
最重要的是我们无法更改 页面的定义方式。 IE。无法使用上一页最后一项日期的行号:只能接受 field2
值。
当前分页算法的实现方式非常丑陋:
1) 上面的查询包装在附加 select 和 row_number()
附加列中,然后包装在存储过程 union_wrapper
中 returns 适当
table ( field1 ..., field2 character varying)
,
2) 然后复杂 select 执行:
RETURN QUERY
with tmp as (
select
rownum, field1, field2 from union_wrapper()
)
SELECT field1, field2
FROM tmp
WHERE rownum > (SELECT rownum
FROM tmp
WHERE field2 = last_field_id
LIMIT 1)
LIMIT page_size
问题是我们必须在内存中构建完整的 union-select 结果,以便稍后检测我们要从中剪切新页面的行号。这非常慢,并且需要花费不可接受的大量时间来执行。
是否有任何方法可以重新配置此操作以显着降低查询复杂性并提高其速度?
再说一遍:我们不能改变分页的条件,我们不能改变表的结构。行检索的唯一方式。
UPD: 我也不能使用临时表,因为我在数据库的只读副本中工作。
您已成功将自己控制在一个紧要关头。查询及其 ORDER BY
表达式与您的分页要求相矛盾。
ORDER BY order_date
不是 确定性 排序顺序(可能有多行具有相同的 order_date
) - 在你在这里做任何其他事情之前你需要。而且 field2
似乎也不是唯一的。您需要两者:定义确定性排序顺序和页面结束/开始的唯一指示符。理想情况下,指标 匹配 排序顺序。可能是 (order_date, field2)
,两列都定义了 NOT NULL
,以及组合 UNIQUE
。您的限制 "only field2 value is acceptable"
与您的查询相矛盾。
这就是所有之前思考如何获得最佳性能...
有用于分页的行值和多列索引的行之有效的解决方案:
但是从多个来源 tables 的组合中提取会使事情复杂化。优化取决于您的设置细节。
如果您无法获得所需的性能,唯一剩下的选择就是以某种方式具体化查询结果。临时 table、光标、实体化视图 - 最佳工具取决于您的设置细节。
假设我们有来自数十个具有不同结构但相似字段含义的表的复杂联合 select:
SELECT a1.abc as field1,
a1.bcd as field2,
a1.date as order_date,
FROM a1_table a1
UNION ALL
SELECT a2.def as field1,
a2.fff as field2,
a2.ts as order_date,
FROM a2_table a2
UNION ALL ...
ORDER BY order_date
另请注意,结果通常按 "synthetic" 字段 order_date
.
此查询提供了大量行,我们希望处理来自这组行的页面。每个页面由两个参数定义:
- 页面大小
field2
上一页最后一项的值
最重要的是我们无法更改 页面的定义方式。 IE。无法使用上一页最后一项日期的行号:只能接受 field2
值。
当前分页算法的实现方式非常丑陋:
1) 上面的查询包装在附加 select 和 row_number()
附加列中,然后包装在存储过程 union_wrapper
中 returns 适当
table ( field1 ..., field2 character varying)
,
2) 然后复杂 select 执行:
RETURN QUERY
with tmp as (
select
rownum, field1, field2 from union_wrapper()
)
SELECT field1, field2
FROM tmp
WHERE rownum > (SELECT rownum
FROM tmp
WHERE field2 = last_field_id
LIMIT 1)
LIMIT page_size
问题是我们必须在内存中构建完整的 union-select 结果,以便稍后检测我们要从中剪切新页面的行号。这非常慢,并且需要花费不可接受的大量时间来执行。
是否有任何方法可以重新配置此操作以显着降低查询复杂性并提高其速度?
再说一遍:我们不能改变分页的条件,我们不能改变表的结构。行检索的唯一方式。
UPD: 我也不能使用临时表,因为我在数据库的只读副本中工作。
您已成功将自己控制在一个紧要关头。查询及其 ORDER BY
表达式与您的分页要求相矛盾。
ORDER BY order_date
不是 确定性 排序顺序(可能有多行具有相同的 order_date
) - 在你在这里做任何其他事情之前你需要。而且 field2
似乎也不是唯一的。您需要两者:定义确定性排序顺序和页面结束/开始的唯一指示符。理想情况下,指标 匹配 排序顺序。可能是 (order_date, field2)
,两列都定义了 NOT NULL
,以及组合 UNIQUE
。您的限制 "only field2 value is acceptable"
与您的查询相矛盾。
这就是所有之前思考如何获得最佳性能...
有用于分页的行值和多列索引的行之有效的解决方案:
但是从多个来源 tables 的组合中提取会使事情复杂化。优化取决于您的设置细节。
如果您无法获得所需的性能,唯一剩下的选择就是以某种方式具体化查询结果。临时 table、光标、实体化视图 - 最佳工具取决于您的设置细节。