X-Original-To: pgsql-performance-postgresql.org@postgresql.org Received: from localhost (wm.hub.org [200.46.204.128]) by postgresql.org (Postfix) with ESMTP id 2901F9F9F7A for ; Mon, 18 Sep 2006 21:07:40 -0300 (ADT) Received: from postgresql.org ([200.46.204.71]) by localhost (mx1.hub.org [200.46.204.128]) (amavisd-new, port 10024) with ESMTP id 08737-04 for ; Tue, 19 Sep 2006 00:07:09 +0000 (UTC) X-Greylist: delayed 00:18:49.055202 by SQLgrey- Received: from svr4.postgresql.org (svr4.postgresql.org [66.98.251.159]) by postgresql.org (Postfix) with ESMTP id 3383F9FB226 for ; Mon, 18 Sep 2006 21:07:08 -0300 (ADT) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.3 Received: from extranet.squiz.net (extranet.squiz.net [203.222.154.144]) by svr4.postgresql.org (Postfix) with ESMTP id E0BF15AF88E for ; Mon, 18 Sep 2006 23:48:16 +0000 (GMT) Received: from localhost (localhost [127.0.0.1]) by extranet.squiz.net (Postfix) with ESMTP id 3A2AA77FFE; Tue, 19 Sep 2006 09:48:11 +1000 (EST) Received: from extranet.squiz.net ([127.0.0.1]) by localhost (intranet [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 26675-07; Tue, 19 Sep 2006 09:48:11 +1000 (EST) Received: from [192.168.0.235] (unknown [192.168.0.235]) by extranet.squiz.net (Postfix) with ESMTP id 1229277BC2 for ; Tue, 19 Sep 2006 09:48:11 +1000 (EST) Message-ID: <450F303A.6090603@squiz.net> Date: Tue, 19 Sep 2006 09:48:10 +1000 From: Marc McIntyre User-Agent: Thunderbird 1.5.0.7 (Macintosh/20060909) MIME-Version: 1.0 To: pgsql-performance@postgresql.org Subject: LIKE query problem Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-Virus-Scanned: by amavisd-new-20030616-p10 (Debian) at squiz.net X-Virus-Scanned: Maia Mailguard 1.0.1 X-Spam-Status: No, hits=1.384 tagged_above=0 required=5 tests=SPF_SOFTFAIL X-Spam-Level: * X-Archive-Number: 200609/238 X-Sequence-Number: 20924 I'm having a problem with a simple query, that finds children of a node, using a materialized path to the node. The query: select n1.id from nodes n1, nodes n2 where n1.path like n2.path || '%' and n2.id = 14; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop (cost=0.00..120256.56 rows=17517 width=4) (actual time=0.901..953.485 rows=7 loops=1) Join Filter: (("inner".path)::text ~~ (("outer".path)::text || '%'::text)) -> Index Scan using nodes_id on nodes n2 (cost=0.00..35.08 rows=11 width=34) (actual time=0.050..0.059 rows=1 loops=1) Index Cond: (id = 14) -> Seq Scan on nodes n1 (cost=0.00..6151.89 rows=318489 width=38) (actual time=0.010..479.479 rows=318489 loops=1) Total runtime: 953.551 ms (6 rows) I've tried re-writing the query, which results in a different plan: select id from nodes where path like ( select path from nodes where id = 14 limit 1 ) || '%'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------ Seq Scan on nodes (cost=3.19..7747.52 rows=1592 width=4) (actual time=0.230..226.311 rows=7 loops=1) Filter: ((path)::text ~~ (($0)::text || '%'::text)) InitPlan -> Limit (cost=0.00..3.19 rows=1 width=34) (actual time=0.018..0.019 rows=1 loops=1) -> Index Scan using nodes_id on nodes (cost=0.00..35.08 rows=11 width=34) (actual time=0.016..0.016 rows=1 loops=1) Index Cond: (id = 14) Total runtime: 226.381 ms (7 rows) While the plan looks a little better, the estimated rows are woefully inaccurate for some reason, resulting in a seq scan on nodes. If I perform the nested select in the second query separately, then use the result in the outer select, it's extremely fast: test=# select path from nodes where id = 14; path -------- /3/13/ (1 row) Time: 0.555 ms test=# select id from nodes where path like '/3/13/%'; id --------- 14 169012 15 16 17 169219 169220 (7 rows) Time: 1.062 ms I've vacuum full analyzed. PG version is 8.1.4 The nodes table is as follows: test=# \d nodes Table "public.nodes" Column | Type | Modifiers --------+-------------------------+----------- id | integer | not null path | character varying(2000) | not null depth | integer | not null Indexes: "nodes_pkey" PRIMARY KEY, btree (id, path) "nodes_id" btree (id) "nodes_path" btree (path) test# select count(*) from nodes; count -------- 318489 Is there a way to perform this efficiently in one query ?