Steve Spicklemire wrote:

Here is the function body... the data is stored in and XML "pickle". I had hoped that it would only be called in building the index.

Since the query uses it in the 'filter' step.. I'm not sure if it's using the index or not.

Does marking the function immutable help the planner know whether it can use the index or not?

Well, since you've got an index using it, you *must* have already marked it immutable. Presumably it's not genuinely immutable though.

CCOC=# \df+ get_cem_for_directBurial
                                                      List of functions
Schema | Name | Result data type | Argument data types | Owner | Language | Source code | Description --------+--------------------------+------------------+---------------------+---------+----------+-------------+------------- public | get_cem_for_directburial | text | character varying | webuser | plpgsql |
    DECLARE
        personID ALIAS for $1;
        qResult RECORD;

    BEGIN
SELECT INTO qResult get_xml_value('/params/param/value/struct/member/*[contains(text(),''cemid'')]/parent::*/value/string/text()','People',personID,'') as cem;
        return qResult.cem;
    END;

That might be stable, but I don't see how it could be immutable unless the xml is in your "people" table.

CCOC=# \d people
                                                     Table "public.people"
Column | Type | Modifiers ------------+-----------------------------+------------------------------------------------------------------------------------ personid | character varying(40) | not null default ('AUTO'::text || (nextval(('People_seq'::text)::regclass))::text)
 modified   | timestamp without time zone | default now()
 created    | timestamp without time zone | default now()
 enabled    | boolean                     |
 first      | character varying(40)       |
 middle     | character varying(15)       |
 last       | character varying(80)       |
 gender     | character varying(2)        |
 sbirthdate | character varying(30)       |
 sdeathdate | character varying(30)       |
 status     | character varying(30)       |
Indexes:
    "people_pkey" PRIMARY KEY, btree (personid)
    "idx_people_cemid" btree (get_cem_for_directburial(personid))
"idx_people_lower_concat3_last" btree (lower_concat3("last", "first", (middle::text || personid::text)::character varying)) "idx_people_servicenum" btree (get_numeric_servicenumber_for_personid(personid))
    "idx_people_status" btree (status)
    "idx_people_take4_personid_" btree (take4(personid))

Filter: (('STJ'::text = get_cem_for_directburial(personid)) AND ((status)::text <> 'R'::text) AND ((status)::text <> 'F'::text))

Filter: (('HCC'::text = get_cem_for_directburial(personid)) AND ((status)::text <> 'R'::text) AND ((status)::text <> 'F'::text))

If the query is actually calling get_cem_for_directburial during the query... then I'm sunk. I'll have to move that data to a regular indexed field. I can do that... it's just a lot of work. ;-(

Where you see it calling "Filter" it's evaluating the function I'm afraid. It's possible for the executor to call the function more than once too.

You've got so much data hidden behind functions, it's unlikely you'll get the planner to make any informed decisions as to the quickest plan.

You're testing for inequality on status, so unless <> F / R is uncommon a partial query on that probably won't help much either.

Just to be sure, cheat. Run get_cem_for_directburial() over the whole people table and dump the results into a cache table. Then, try the same query with a function that just does a table lookup - see if that makes things faster.

Then I'd just write a couple of trigger functions to keep the cache table up-to-date and join against it. That will let the planner see common values and make better predictions for its plans.


If you (or anyone else) would like to explore functional indexes and calling, I've attached a small demo script.

--
  Richard Huxton
  Archonet Ltd
BEGIN;

CREATE TABLE fit (
	a SERIAL,
	b int4,
	PRIMARY KEY (a)
);

CREATE SEQUENCE dummy_seq;

CREATE FUNCTION fit_fn(id int4) RETURNS int4 AS $$
BEGIN
	PERFORM nextval('dummy_seq');
	RETURN (id % 100);
END;
$$ LANGUAGE plpgsql IMMUTABLE;

CREATE INDEX fit_fn_idx ON fit ( fit_fn(a) );

INSERT INTO fit SELECT generate_series(1,1000), round(random() * 100);

COMMIT;

VACUUM ANALYSE fit;

SELECT nextval('dummy_seq');
EXPLAIN ANALYSE SELECT a,b FROM fit WHERE fit_fn(a) = 7;
SELECT nextval('dummy_seq');
EXPLAIN ANALYSE SELECT a,b FROM fit WHERE fit_fn(a) = b;
SELECT nextval('dummy_seq');

---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

               http://archives.postgresql.org/

Reply via email to