PostgreSQL 执行多个动态 WHERE 条件而不动态编写 SQL
Posted
技术标签:
【中文标题】PostgreSQL 执行多个动态 WHERE 条件而不动态编写 SQL【英文标题】:PostgreSQL performing multiple dynamic WHERE conditions without dynamically writing SQL 【发布时间】:2013-07-27 12:28:44 【问题描述】:我有一个案例,用户可以指定任意数量的参数,这些参数将根据表格进行过滤。简单地说,有一系列参数,每个参数有 64 个桶。总之,这代表了一个线性的数字序列。每条记录包含任意数量的桶点。
此外,这些数字在每个桶的范围内。
用户可以指定任意数量的任意记录的期望值范围。返回所有指定参数(桶)重叠的记录。
您会注意到有一个低点和一个高点。这是范围。通过查看是否有重叠,我可以比使用范围查询更快地获得结果。这是一种优化技术。
这里有两个条件的例子:
SELECT id
FROM mytable2
WHERE (val_low && (ARRAY(SELECT generate_series((0 * 64) + 20, (0 * 64) + 28))) OR
val_high && (ARRAY(SELECT generate_series((0 * 64) + 20, (0 * 64) + 28))))
AND (val_low && (ARRAY(SELECT generate_series((1 * 64) + 12, (1 * 64) + 15))) OR
val_high && (ARRAY(SELECT generate_series((1 * 64) + 12, (1 * 64) + 15))))
val_low
和 val_high
存储桶针对指定范围的数组进行交集测试。
问题是我必须在函数中动态构建这个查询。参数列表被传递给函数(作为用户定义类型[数组]),查询动态生成,然后执行。
它有效,但我希望能够做到这一点,而不必在函数中编写 SQL。
具体来说,该函数将传递一个自定义类型数组,如下所示:
param_num int,
val_low int,
val_high int
generate_series 函数调用中的值为(param_num * 64) + val_low, (param_num * 64) + val_high
。
这可能吗?
样本数据创建:
DROP TABLE IF EXISTS
mytable2;
CREATE TABLE
mytable2
(
id INT NOT NULL PRIMARY KEY,
val_low int[],
val_high int[]
);
SELECT SETSEED(0.20130725);
WITH t AS
(
SELECT id,
1 + FLOOR(RANDOM() * 24) AS l1, (RANDOM() * 8)::int AS h1,
1 + FLOOR(RANDOM() * 24) AS l2, (RANDOM() * 8)::int AS h2,
1 + FLOOR(RANDOM() * 24) AS l3, (RANDOM() * 8)::int AS h3,
1 + FLOOR(RANDOM() * 24) AS l4, (RANDOM() * 8)::int AS h4
FROM generate_series(1, 500000) id
)
INSERT
INTO mytable2
SELECT T.id, array[t.l1, (1 * 64) + t.l2, (2 * 64) + t.l3, (3 * 64) + t.l4],
array[t.l1 + t.h1, (1 * 64) + t.l2 + t.h2, (2 * 64) + t.l3 + t.h3,
(3 * 64) + t.l4 + t.h4]
FROM T;
CREATE INDEX
ix_mytable2_vhstore_low
ON mytable2
USING GIN (val_low);
CREATE INDEX
ix_mytable2_vhstore_high
ON mytable2
USING GIN (val_high);
示例查询:
--EXPLAIN ANALYZE
SELECT COUNT(1)
FROM
(
SELECT id
FROM mytable2
WHERE (val_low && (ARRAY(SELECT generate_series(20, 28))) OR val_high &&
(ARRAY(SELECT generate_series(20, 28))))
AND (val_low && (ARRAY(SELECT generate_series((1 * 64) + 12, (1 * 64) + 15)))
OR val_high && (ARRAY(SELECT generate_series((1 * 64) + 12, (1 * 64) + 15))))
) m;
结果:54983
【问题讨论】:
我没有看到正在使用的表值。这意味着将返回所有行或不返回任何行。对吗? 返回 val_low 或 val_high 与所提供系列的所有重叠的所有行。 【参考方案1】:SQL Fiddle
with s as (
select array(select generate_series(
a[i][1] * 64 + a[i][2], a[i][1] * 64 + a[i][3]
)) as a
from
(values (array[[0,20,28],[1,12,15]])) s(a)
cross join
generate_series(1, array_length(array[[0,20,28],[1,12,15]], 1)) g(i)
)
select id
from mytable2 cross join s
group by id
having count((not(val_low && a or val_high && a)) or null) = 0
array[[0,20,28],[1,12,15]]
是传入的参数
【讨论】:
谢谢。我将不得不使用它一段时间,因为它返回的记录比它应该的要多。 源数据为范围类型。 随机播种,所以结果是一样的。但是,您可以使用查询示例查看结果应该是什么,并与您的新函数进行比较。 setseed 使它成为伪随机的,所以我们都会得到相同的集合。但是,这不是问题。您可以比较两个查询的结果,如果相同,则您的结果有效。 删除数组的“[1,12,15]”部分(只留下第一段),你会看到 0 结果,而不是 187168。另外,在小提琴页面上,您的来源是 int,而不是 int[],所以我不确定它是否会处理相同的内容。以上是关于PostgreSQL 执行多个动态 WHERE 条件而不动态编写 SQL的主要内容,如果未能解决你的问题,请参考以下文章
PostgreSQL 中的 SQL JOIN - WHERE 子句中的执行计划与 ON 子句中的不同