您甚至可以将 EXISTS() 条件包装在 pure SQL 函数中,它只会执行一次:
-- \i tmp.sql
CREATE TABLE omg
( id integer NOT NULL PRIMARY KEY
, must_pay integer NOT NULL
);
INSERT INTO omg(id, must_pay) VALUES(1,0);
CREATE FUNCTION owe_money() RETURNS BOOLEAN AS
$func$
SELECT EXISTS(SELECT 1
FROM omg o
WHERE o.must_pay > 0
);
$func$
-- language sql;
language sql STABLE;
EXPLAIN
SELECT owe_money();
INSERT INTO omg(id, must_pay) VALUES(2,100);
EXPLAIN
SELECT owe_money();
EXPLAIN
SELECT * FROM omg
WHERE owe_money();
如果您将关键字STABLE 添加到函数定义中,DBMS 将知道返回值将不会更改(在同一事务/语句中)。如果没有STABLE,该函数将每行调用一次。
QUERY PLAN
-------------------------------------------------------------------------------------------------
Seq Scan on omg (cost=0.00..566.40 rows=713 width=8) (actual time=0.303..0.331 rows=2 loops=1)
Filter: owe_money()
Total runtime: 0.384 ms
(3 rows)
STABLE 函数的结果是一次性过滤器:
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Result (cost=0.25..31.65 rows=2140 width=8) (actual time=0.304..0.316 rows=2 loops=1)
One-Time Filter: owe_money()
-> Seq Scan on omg (cost=0.25..31.65 rows=2140 width=8) (actual time=0.004..0.009 rows=2 loops=1)
Total runtime: 0.379 ms
(4 行)
与不相关子查询相同的条件本质上产生与 STABLE 函数相同的计划(现在子查询执行一次,其结果 ($0) 在一次性过滤器中进行测试):
EXPLAIN ANALYZE
SELECT * FROM omg
WHERE EXISTS (
SELECT 1
FROM omg o
WHERE o.must_pay > 0
);
QUERY PLAN
----------------------------------------------------------------------------------------------------------
Result (cost=0.05..31.45 rows=2140 width=8) (actual time=0.022..0.034 rows=2 loops=1)
One-Time Filter: $0
InitPlan 1 (returns $0)
-> Seq Scan on omg o (cost=0.00..36.75 rows=713 width=0) (actual time=0.011..0.011 rows=1 loops=1)
Filter: (must_pay > 0)
Rows Removed by Filter: 1
-> Seq Scan on omg (cost=0.00..31.40 rows=2140 width=8) (actual time=0.003..0.008 rows=2 loops=1)
Total runtime: 0.081 ms
(8 rows)
但是我们不是还在扫描表格,如果该函数返回 false,则基本上执行 where false 每一行,或者如果该函数返回 true,则执行 where true?
EXPLAIN ANALYZE
SELECT * FROM omg
WHERE EXISTS (
SELECT 1
FROM omg o
WHERE o.id < 0
);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Result (cost=1.66..3.66 rows=100 width=8) (actual time=0.011..0.011 rows=0 loops=1)
One-Time Filter: $0
InitPlan 1 (returns $0)
-> Index Only Scan using omg_pkey on omg o (cost=0.14..1.66 rows=1 width=0) (actual time=0.006..0.006 rows=0 loops=1)
Index Cond: (id < 0)
Heap Fetches: 0
-> Seq Scan on omg (cost=0.00..2.00 rows=100 width=8) (never executed)
Total runtime: 0.063 ms
(8 rows)
从未执行过说明了一切。