Thread: expression index not used within function

expression index not used within function

From
LPlateAndy
Date:
Hi,

I have created an index as follows:

(replace(lower(my_column), ' '::text, ''::text)

which i use in a WHERE clause against LIKE 'string%'

By using text_pattern_ops i get the index used provided i more than one
character is used in the string.


However, with the same SELECT query running within a function (using RETURNS
TABLE) the query takes significantly longer - as though the index is
ignored. e.g. 2500ms instead of 12ms

Is there something fundamental i'm missing about the use of the indexed
expression?

Thanks!

Andy



--
View this message in context:
http://postgresql.1045698.n5.nabble.com/expression-index-not-used-within-function-tp5778236.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: expression index not used within function

From
LPlateAndy
Date:
Hi,

I have further found that it is only when passing the string in to the
function that the slow response occurs.

When i call SELECT * FROM _function(IN TEXT) it is very slow on my WHERE
clause using LIKE($1||'%') but (run in error), very fast when LIKE('some
text'||'%')

I have also created and indexed a new column to eliminate the expression and
the same happens

Andy


Hi,

I have created an index on an expression as follows:

(replace(lower(my_column), ' '::text, ''::text)

which i use in a WHERE clause against LIKE 'string%'

By using text_pattern_ops i get the index used provided i more than one
character is used in the string.


However, with the same SELECT query running within a function (using RETURNS
TABLE) the query takes significantly longer - as though the index is
ignored. e.g. 2500ms instead of 12ms






--
View this message in context:
http://postgresql.1045698.n5.nabble.com/expression-index-not-used-within-function-tp5778236p5778241.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: expression index not used within function

From
David Johnston
Date:
LPlateAndy wrote
> When i call SELECT * FROM _function(IN TEXT) it is very slow on my WHERE
> clause using LIKE($1||'%') but (run in error), very fast when LIKE('some
> text'||'%')

The index cannot be used for LIKE ($1 || '%') because there is no way the
planner can guarantee the value of $1 isn't something like "%mid" which
would resolve to "LIKE (%mid%)" which is a mid-string search which the index
will not help with.

If you place a constant at the front of the like pattern it can use the
index to get into the region with the matching prefix.

David J.




--
View this message in context:
http://postgresql.1045698.n5.nabble.com/expression-index-not-used-within-function-tp5778236p5778242.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: expression index not used within function

From
Tom Lane
Date:
David Johnston <polobo@yahoo.com> writes:
> LPlateAndy wrote
>> When i call SELECT * FROM _function(IN TEXT) it is very slow on my WHERE
>> clause using LIKE($1||'%') but (run in error), very fast when LIKE('some
>> text'||'%')

> The index cannot be used for LIKE ($1 || '%') because there is no way the
> planner can guarantee the value of $1 isn't something like "%mid" which
> would resolve to "LIKE (%mid%)" which is a mid-string search which the index
> will not help with.

> If you place a constant at the front of the like pattern it can use the
> index to get into the region with the matching prefix.

Also, if you use 9.2 or later, the planner should be able to get the
desired result by re-planning the statement each time (so that it can
treat the current value of $1 as a constant).  If this is 9.2+, and
that doesn't seem to be happening, it would be worth presenting a
complete example so that we can diagnose why not.

(Pre-9.2, the traditional advice for forcing a custom plan each time
is to use EXECUTE.  That's not the optimal way anymore, though.)

            regards, tom lane


Re: expression index not used within function

From
LPlateAndy
Date:
Hi David,

Thanks, i can see the logic there. To place a constant in front, i tried
putting 'pc'||pcode in to the index and 'pc'||$1 in to the WHERE clause. It
had no effect - does the planner see this as a cheat and i need to actually
prefix the data in the tables?

Andy





--
View this message in context:
http://postgresql.1045698.n5.nabble.com/expression-index-not-used-within-function-tp5778236p5778319.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: expression index not used within function

From
LPlateAndy
Date:
Thanks Tom. I'll try the "EXECUTE" method as well but my dev environment is
9.2 and the planner doesn't seem to be including the index so following are
the fairly basic table/index/function details. Thanks, Andy:


================================
TABLE (circa 300,000 rows):
================================

CREATE TABLE postcode
(
  gid serial NOT NULL,
  pcode text,
  e integer,
  n integer,
  geometry geometry(Geometry,27700),
  CONSTRAINT postcode_pkey PRIMARY KEY (gid)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE postcode
  OWNER TO postgres;

================================
TABLE INDEX:
================================

CREATE INDEX idx_postcode_lc_pcode
  ON postcode
  USING btree
  (replace(lower(pcode), ' '::text, ''::text) COLLATE pg_catalog."default"
text_pattern_ops);

================================
SELECT FUNCTION:
================================

CREATE OR REPLACE FUNCTION _search_pcode(IN text)
  RETURNS TABLE(searchmatch text, geometry geometry) AS
$BODY$
SELECT pcode searchmatch, geometry  FROM postcode
WHERE (replace(lower(pcode), ' '::text, ''::text)) LIKE
(replace((lower($1)::text),' '::text,''::text)||'%'::text)
LIMIT 20;

$BODY$
  LANGUAGE sql IMMUTABLE SECURITY DEFINER
  COST 100
  ROWS 1000;
ALTER FUNCTION _search_pcode(text)
  OWNER TO postgres;
GRANT EXECUTE ON FUNCTION _search_pcode(text) TO public;
GRANT EXECUTE ON FUNCTION _search_pcode(text) TO postgres;





--
View this message in context:
http://postgresql.1045698.n5.nabble.com/expression-index-not-used-within-function-tp5778236p5778321.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: expression index not used within function

From
LPlateAndy
Date:
Hi,

Just wondering what kind of execute statement (within a function) i should
use to force the planner to use the index for the following?:

SELECT pcode searchmatch, geometry  FROM postcode
WHERE (replace(lower(pcode), ' '::text, ''::text)) LIKE
(replace((lower($1)::text),' '::text,''::text)||'%'::text)

Thanks

Andy



--
View this message in context:
http://postgresql.1045698.n5.nabble.com/expression-index-not-used-within-function-tp5778236p5778927.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: expression index not used within function

From
Albe Laurenz
Date:
LPlateAndy wrote:
> Just wondering what kind of execute statement (within a function) i should
> use to force the planner to use the index for the following?:

You cannot force anything.
The best you can do is to provide an index that *can* be used
and keep your statistics accurate.

> SELECT pcode searchmatch, geometry  FROM postcode
> WHERE (replace(lower(pcode), ' '::text, ''::text)) LIKE
> (replace((lower($1)::text),' '::text,''::text)||'%'::text)

I assume that pcode is of type text.

In that case you could create an index like

CREATE INDEX my_index ON table_name ((replace(lower(pcode), ' '::text, ''::text)) text_pattern_ops);
ANALYZE table_name;

Such an index can be used for queries with a LIKE, if you
have a constant on the right hand side that does not
start with a wildcard.

If you have PostgreSQL 9.2 or later, that might work out of
the box in a PL/pgSQL function.

In doubt, or if you have an older version, first compute
the right hand side and run the query with EXECUTE.

Yours,
Laurenz Albe