可以在PostgreSQL函数中缓存变量吗?

时间:2018-11-02 09:25:09

标签: postgresql function plpgsql

上下文

我正在构建一个Postgres扩展,它增加了使用语言变量进行模糊查询的可能性。例如:

SELECT age~=('age'|>'adult') FROM people;

将返回一个人是成人这一事实的合理性(成人定义为梯形函数30/40~60\65

问题

我做了一个函数,可以返回指定值和语言变量的语言名称:

SELECT age, age~>'age' FROM people;

返回

 age | age~>'age'
-----+--------------
 20  | young adult
 10  | child
 45  | adult
 60  | old

此函数和运算符的来源如下:

CREATE FUNCTION get_fuzzy_name(
  input FLOAT8,
  type_name VARCHAR(64)
) RETURNS VARCHAR(64) AS $$
DECLARE
  type_id fuzzy.types.id%TYPE;
  deg FLOAT8;
  result_name VARCHAR(64);
BEGIN
  type_id := get_fuzzy_type(type_name); -- returns type's id based on it's name
  SELECT
      degree(input, fun) AS d, name
    INTO
      deg, result_name
    FROM fuzzy.functions
    WHERE type=type_id
    ORDER BY d DESC LIMIT 1;
  IF deg=0 THEN
    RETURN NULL;
  END IF;
  RETURN result_name;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;

CREATE OPERATOR ~> (
  PROCEDURE = get_fuzzy_name,
  LEFTARG = FLOAT8,
  RIGHTARG = VARCHAR(64)
);

问题在于,对于每行,上面的函数都会查询模糊类型,并一次又一次地起作用。因此,我提出了这一点,作为改进的基础(模糊函数保存在变量中):

CREATE TYPE FUZZY_TYPE_FUNCTION AS (
  func  TRAPEZOIDAL_FUNCTION,
  range_name VARCHAR(64)
);
CREATE FUNCTION get_fuzzy_name(
  input FLOAT8,
  type_name VARCHAR(64)
) RETURNS VARCHAR(64) AS $$
DECLARE
  f FUZZY_TYPE_FUNCTION;
  _type_functions FUZZY_TYPE_FUNCTION[] := array(SELECT (fun, name) FROM fuzzy.functions WHERE fuzzy.functions.type=type_name);
  _deg_tmp FLOAT8;
  _deg FLOAT8;
  _result_name VARCHAR(64);
BEGIN
  _deg := 0;
  FOREACH f IN array(_type_functions) LOOP
    _deg_tmp := degree(input, f.func);
    RAISE NOTICE '% && % = %', f, input, _deg_tmp;
    IF _deg<_deg_tmp THEN
      _deg := _deg_tmp;
      _result_name := f.range_name;
      EXIT WHEN _deg=1;
    END IF;
  END LOOP;
  IF _deg=0 THEN
    RETURN NULL;
  END IF;
  RETURN _result_name;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;

是否有一种方法可以使每个查询一次获取函数表的值并将其缓存,以便可以重用它并极大地加快整个查询的速度?

其他信息

根据要求,这些表是:

CREATE SCHEMA IF NOT EXISTS fuzzy;

CREATE TABLE IF NOT EXISTS fuzzy.types (
  id   SERIAL PRIMARY KEY,
  name VARCHAR(64) UNIQUE
);

CREATE TABLE IF NOT EXISTS fuzzy.functions (
  type INT                  NOT NULL,
  fun  TRAPEZOIDAL_FUNCTION NOT NULL,
  name VARCHAR(64),
  FOREIGN KEY (type) REFERENCES fuzzy.types (id) ON DELETE CASCADE,
  UNIQUE (type, name)
);

fuzzy.types可能会包含几行,这些行是id-name对, fuzzy.functions最有可能在每种类型中包含3到10行,对于重度用例,我可能会包含约500行。

1 个答案:

答案 0 :(得分:1)

您可能会基于一些关于功能性能的误导性假设。

请尝试使用此简化的SQL函数:

CREATE FUNCTION get_fuzzy_name(_input FLOAT8, _type_name VARCHAR(64))
  RETURNS VARCHAR(64) AS
$func$
   SELECT f.name
   FROM   fuzzy.functions f
   JOIN   fuzzy.types     t ON t.id = f.type
   WHERE  t.name = _type_name
   AND    degree(_input, f.fun) > 0
   ORDER  BY degree(_input, f.fun) DESC
   LIMIT  1;
$func$  LANGUAGE sql STABLE;
  • LANGUAGE sql。没有变量,赋值,IF构造,... 1个简单查询。 完全重写,但应等效。

  • STABLE,而不是IMMUTABLE

  • 根本没有嵌套函数调用。替换为联接。应该便宜些。

  • 但是,内联未公开的degree()函数也可能更便宜。 May 甚至可以简化为更快的“最近邻居”查询。信息不足。

  • 此函数可以内联,而不是原始函数。 我删除了STRICT,可能是这样。无法判断,信息不足。

请参见Postgres Wiki about inlining of scalar functions
并且:

相关: