sql - 查询当前表时,Postgresql 插入触发器变慢

标签 sql database postgresql plpgsql

在表中插入批号时,我们会计算基数存在的次数,并根据该计数在新编号的末尾添加 -##。

我已经去掉了大部分逻辑(我们也检查其他内容)。我也知道这里会跳过 -1 的逻辑缺陷。

-- Function: stone._lsuniqueid()

-- DROP FUNCTION stone._lsuniqueid();

CREATE OR REPLACE FUNCTION stone._lsuniqueid()
  RETURNS trigger AS
$BODY$
DECLARE
 _count INTEGER;

BEGIN
  -- Obtain the number of occurences of this new ls_number
  SELECT COUNT(ls_number) into _count
  FROM ls
  WHERE ls_number LIKE CAST(NEW.ls_number || '%' AS text);

  -- Allow new ls_numbers to be entered as is, otherwise add "-#{count + 1}"
  -- to the end of the ls_number
  if _count > 0 THEN
    NEW.ls_number = NEW.ls_number || '-' || CAST(_count + 1 AS text);
  END IF;      

  RETURN NEW;
END
$BODY$

INSERT INTO ls VALUES (NEXTVAL('ls_ls_id_seq'),7285,UPPER('20151012'));
--> Query returned successfully: one row affected, 391 ms execution time.

计数查询非常快

SELECT COUNT(ls_number)
FROM ls
WHERE ls_number LIKE CAST('20151012' || '%' AS text);
--> 19ms

为了比较,我尝试了一个类似的触发器,但对具有相同行数和类似查询时间的不同表运行了计数。

SELECT COUNT(lsdetail_id)
FROM lsdetail
WHERE lsdetail_id > 2433308
--> 20ms

运行相同的插入并针对不同的表运行计数返回结果快 20 倍。

INSERT INTO ls VALUES (NEXTVAL('ls_ls_id_seq'),7285,UPPER('20151012'));
 --> Query returned successfully: one row affected, 20 ms execution time.

ls 表有大约 250 万行

我尝试了几种不同的方法,问题似乎出在从我插入的同一个表中进行选择时。

我想知道为什么会这样,但我也愿意接受一种更好的方法来创建“子批处理”编号。

谢谢!

最佳答案

在这里找到答案: http://www.postgresql.org/message-id/27705.1150381444@sss.pgh.pa.us

回复:如何分析函数性能

"Mindaugas" writes:

Is it possible to somehow analyze function performance? E.g. we are using function cleanup() which takes obviously too much time to execute but I have problems trying to figure what is slowing things down.

When I explain analyze function lines step by step it show quite acceptable performance.

--

Are you sure you are "explain analyze"ing the same queries the function is really doing? You have to account for the fact that what plpgsql is issuing is parameterized queries, and sometimes that limits the planner's ability to pick a good plan. For instance, if you have

declare x int;
begin
    ...
    for r in select * from foo where key = x loop ...

then what is really getting planned and executed is "select * from foo where key = $1" --- every plpgsql variable gets replaced by a parameter symbol "$n". You can model this for EXPLAIN purposes with a prepared statement:

prepare p1(int) as select * from foo where key = $1;
explain analyze execute p1(42);

If you find out that a particular query really sucks when parameterized, you can work around this by using EXECUTE to force the query to be planned afresh on each use with literal constants instead of parameters:

然后我调查了这个: http://www.postgresql.org/docs/9.1/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN

39.5.4. Executing Dynamic Commands

Oftentimes you will want to generate dynamic commands inside your PL/pgSQL functions, that is, commands that will involve different tables or different data types each time they are executed. PL/pgSQL's normal attempts to cache plans for commands (as discussed in Section 39.10.2) will not work in such scenarios. To handle this sort of problem, the EXECUTE statement is provided:

EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2'
INTO c
USING checked_user, checked_date;

--

所以最后是更新计数选择的问题:

 EXECUTE 'SELECT COALESCE(COUNT(ls_number), 0) FROM ls WHERE ls_number LIKE $1 || ''%'';'
 INTO _count
 USING NEW.ls_number;

关于sql - 查询当前表时,Postgresql 插入触发器变慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33092618/

相关文章:

node.js - Postman 卡在 "GET"请求,在使用 PostgreSQL 作为后端的 Node JS 中给出 "Could not get response Error: read ECONNRESET"错误

sql - 使用 RANGE 模式理解窗函数框架

sql - 每周汇总最近加入的记录

sql - 最好的 Oracle SQL 查询分析工具

sql - 使用 sql 'like' 命令使用另一个表中的数据删除行

ruby-on-rails - Rails 中两个表作为一个模型

java - 如何优化结果集返回的大量数据

SQL 存储过程不返回任何数据

SQLite HAVING 比较错误

java - Hibernate - 分页的不同结果