纯SQL / PostgreSQL中的元查询过程

时间:2014-09-04 22:35:52

标签: sql postgresql plpgsql dynamic-sql range-types

鉴于以下内容:

CREATE TABLE filters (
id BIGINT NOT NULL
, name TEXT NOT NULL
, range NUMRANGE NOT NULL
, UNIQUE (id, name)
);

INSERT INTO filters (id,name,range) VALUES (99,'value1','[45,90]'::NUMRANGE);
INSERT INTO filters (id,name,range) VALUES (99,'value2','[15,50]'::NUMRANGE);
INSERT INTO filters (id,name,range) VALUES (23,'value1','[45,90]'::NUMRANGE);

CREATE TABLE data (
value1 NUMERIC
, value2 NUMERIC
);

编辑 - 添加了样本数据行

// filter.id = 99, neither value1 or value2 match this row
INSERT INTO data (value1,value2) VALUES (40.01, 11.12); 

// filter.id = 99, only value2 matches this row
INSERT INTO data (value1,value2) VALUES (15.48, 20.14);

// filter.id = 99, only value1 matches this row
INSERT INTO data (value1,value2) VALUES (53.48, 70.14);

// filter.id = 99, both value1 and value2 match this row
INSERT INTO data (value1,value2) VALUES (64.12, 33.48);

过滤器表包含一系列按ID分组的过滤器。

我想将一组特定的过滤器(即filters.id = 99)应用于数据表,其中每行过滤器都会将filters.range应用于匹配filters.name的数据列。

最初,模式在过滤器和数据中包含1:1列重复,这样可以使用filters.col3 = data.col3或通过范围比较来完成连接。问题是我知道有超过1000个过滤器我想在过滤器表中跟踪并应用于数据表。

尝试了一些谷歌搜索,但不知道如何说出这类问题。我知道我可以用外部语言形成查询,但是如果可能的话我想在纯SQL / PostgreSQL中执行它。

1 个答案:

答案 0 :(得分:1)

你需要动态SQL。

为您的表添加代理PK有几个原因,其中一个原因是同一组内过滤器的稳定排序顺序。另一个:总是添加PK。

CREATE TABLE filter (
   filter_id serial PRIMARY KEY  -- add surrogate PK
 , filter_grp int NOT NULL
 , name   text NOT NULL
 , range  numrange NOT NULL
);

此功能 all

CREATE OR REPLACE FUNCTION f_get_data(_filter_grp int)
  RETURNS SETOF data AS
$func$
DECLARE
   _where text;
   _arr   numrange[];
BEGIN

SELECT string_agg(name || ' <@ $1[' || rn || ']', ' AND ') -- AND?
      ,array_agg(range)
INTO   _where, _arr
FROM  (
   SELECT name, range
         ,row_number() OVER (ORDER BY filter_id) AS rn
   FROM   filter
   WHERE  filter_grp = _filter_grp
   ORDER  BY filter_id
   ) sub;

-- RAISE NOTICE '%', 'SELECT * FROM data WHERE ' || _where;
RETURN QUERY EXECUTE 'SELECT * FROM data WHERE ' || _where
USING  _arr;

END
$func$ LANGUAGE plpgsql;

呼叫:

SELECT * FROM f_get_data(99);

这构建并执行以下形式的查询:

SELECT * FROM data
WHERE  value1 <@'[45,90]'::numrange
AND    value2 <@'[15,50]'::numrange;

SQL Fiddle.

解释

  • SELECT * FROM data可以方便地与SETOF data结合作为函数的返回类型。

  • 在构建查询字符串时,避免将值转换为文本并返回。而是使用USING子句提供值,并在$n的查询中使用EXECUTE占位符。
    在这种特殊情况下,值的数量也是动态的。我们可以通过使用数组索引来回避子弹,这些索引自动对应于聚合数组中值的位置。