Обсуждение: Results of stored procedures in WHERE clause

Поиск
Список
Период
Сортировка

Results of stored procedures in WHERE clause

От
Gordon
Дата:
I have a table representing tree structures of pages on a website.
they have an itm_id column (integer key) and an itm_parent column
(pointer to item's parent node).  Any item with an itm_parent of 0 is
a root node, representing a website.  Anything with a non-zero parent
is a non-root node representing a folder or document in a website.

I need to be able to do queries that restrict my result set to items
belonging to a specified site and ignore all nodes that belong to
different sites.  To determine the ID of the site an item belongs to I
wrote a stored procedure:

CREATE OR REPLACE FUNCTION cms.getroot(node integer)
  RETURNS integer AS
$BODY$DECLARE
    thisnode    integer := node;
    thisparent    integer    := node;
BEGIN
    WHILE thisparent != 0 LOOP
        SELECT itm_id, itm_parent
        INTO thisnode, thisparent
        FROM cms.cms_items
        WHERE itm_id = thisparent;
    END LOOP;
    RETURN thisnode;
END;
$BODY$
  LANGUAGE 'plpgsql' STABLE
  COST 100;

This returns the ID of the root node for non-root nodes, the node's
own ID for root-nodes and NULL for invalid IDs.

I'm writing a query to do document searching (the version given is
simplified to the problem in hand).

SELECT cms_v_items.* ,
getroot (cms_v_items.itm_id) AS itm_root
FROM cms_v_items
WHERE itm_root = ?;

I was hoping this query would return a set of items that had the same
root node.  Instead it throws an error, column itm_root does not
exist.

I'm obviously doing something wrong here, but what?

Re: Results of stored procedures in WHERE clause

От
"Adam Rich"
Дата:
>
> I need to be able to do queries that restrict my result set to items
> belonging to a specified site and ignore all nodes that belong to
> different sites.  To determine the ID of the site an item belongs to I
> wrote a stored procedure:
>
> This returns the ID of the root node for non-root nodes, the node's
> own ID for root-nodes and NULL for invalid IDs.
>
> I'm writing a query to do document searching (the version given is
> simplified to the problem in hand).
>
> SELECT cms_v_items.* ,
> getroot (cms_v_items.itm_id) AS itm_root
> FROM cms_v_items
> WHERE itm_root = ?;
>
> I was hoping this query would return a set of items that had the same
> root node.  Instead it throws an error, column itm_root does not
> exist.
>
> I'm obviously doing something wrong here, but what?
>

I don't think you can reference an alias in the where clause.
You'll have to repeat it, like this:

SELECT cms_v_items.* ,
getroot (cms_v_items.itm_id) AS itm_root
FROM cms_v_items
WHERE getroot (cms_v_items.itm_id) = ?;

Don't worry, I think with the function marked STABLE, postgresql is
smart enough not to call it twice.  I think you could further
optimize your function doing something like this:

SELECT cms_v_items.* ,
getroot (cms_v_items.itm_parent) AS itm_root
FROM cms_v_items
WHERE (itm_parent  = ?
OR getroot (cms_v_items.itm_parent) = ?;

This will save one loop.

Keep in mind, both queries will perform the getroot() function call
for every single row in cms_v_items.  You may want to experiment
with a function that takes the root ID as a parameter and returns
an array or a rowset, of just the items beneath that root.  Then
you'd use that function in your query by joining to the results
or using "= ANY".  This might be faster:

SELECT * from
FROM cms_v_items
WHERE itm_id = ANY(item_in_root(?));


















Re: Results of stored procedures in WHERE clause

От
"Justin Pasher"
Дата:
> -----Original Message-----
> From: pgsql-general-owner@postgresql.org [mailto:pgsql-general-
> owner@postgresql.org] On Behalf Of Gordon
> Sent: Tuesday, May 20, 2008 11:03 AM
> To: pgsql-general@postgresql.org
> Subject: [GENERAL] Results of stored procedures in WHERE clause
>
> I have a table representing tree structures of pages on a website.
> they have an itm_id column (integer key) and an itm_parent column
> (pointer to item's parent node).  Any item with an itm_parent of 0 is
> a root node, representing a website.  Anything with a non-zero parent
> is a non-root node representing a folder or document in a website.
>
> I need to be able to do queries that restrict my result set to items
> belonging to a specified site and ignore all nodes that belong to
> different sites.  To determine the ID of the site an item belongs to I
> wrote a stored procedure:

...

I would highly recommend checking out the ltree contrib module. It will make
this task much easier, as long as you are not locked into the current
database design. Manually performing multi-level parent/child relationships
on a table can become quite painful. You will probably get faster results
using ltree also due to the fact that you can perform what you want with one
query instead of looping through multiple queries (very important if your
tree gets big).

http://www.sai.msu.su/~megera/postgres/gist/ltree/


Justin Pasher


Re: Results of stored procedures in WHERE clause

От
"Roberts, Jon"
Дата:
I'm assuming you are having problems because the tree structure allows
for n levels and you need to get all of the child records.

I am guessing you mean this:

create table cms_items
(itm_id integer primary key not null,
 itm_parent integer default 0 not null,
 url varchar(100) not null);

insert into cms_items values (1, 0, 'postgresql.org');
insert into cms_items values (2, 1, 'foo');
insert into cms_items values (3, 1, 'bar');
insert into cms_items values (4, 0, 'cnn.com');
insert into cms_items values (5, 2, 'foo2');
insert into cms_items values (6, 5, 'foo3');

create or replace function fn_get_root (p_itm_id integer) returns setof
cms_items as
$$
declare
  v_rec cms_items;
  v_rec2 cms_items;
  i integer := 0;
  v_last_itm_id cms_items.itm_id%type;

begin
  <<outside_loop>>
  for v_rec in select * from cms_items where itm_parent = p_itm_id loop
    return next v_rec;
    <<inside_loop>>
    while i is not null loop
      i := i + 1;

      if i = 1 then
        v_last_itm_id := v_rec.itm_id;
      end if;

      select * into v_rec2 from cms_items where itm_parent =
v_last_itm_id;

      if v_rec2.itm_id is not null then
        return next v_rec2;
      else
        i := null;
      end if;

      v_last_itm_id := v_rec2.itm_id;

    end loop inside_loop;
  end loop outside_loop;

end;
$$
language 'plpgsql';


select * from fn_get_root(1);
2;1;"foo"
5;2;"foo2"
6;5;"foo3"
3;1;"bar"

It gets the direct child records and then it also gets the child's child
(foo2) and then the child's child's child (foo3).  It will go all of the
way through the hierarchy too.


Jon
> -----Original Message-----
> From: pgsql-general-owner@postgresql.org [mailto:pgsql-general-
> owner@postgresql.org] On Behalf Of Gordon
> Sent: Tuesday, May 20, 2008 11:03 AM
> To: pgsql-general@postgresql.org
> Subject: [GENERAL] Results of stored procedures in WHERE clause
>
> I have a table representing tree structures of pages on a website.
> they have an itm_id column (integer key) and an itm_parent column
> (pointer to item's parent node).  Any item with an itm_parent of 0 is
> a root node, representing a website.  Anything with a non-zero parent
> is a non-root node representing a folder or document in a website.
>
> I need to be able to do queries that restrict my result set to items
> belonging to a specified site and ignore all nodes that belong to
> different sites.  To determine the ID of the site an item belongs to I
> wrote a stored procedure:
>
> CREATE OR REPLACE FUNCTION cms.getroot(node integer)
>   RETURNS integer AS
> $BODY$DECLARE
>     thisnode    integer := node;
>     thisparent    integer    := node;
> BEGIN
>     WHILE thisparent != 0 LOOP
>         SELECT itm_id, itm_parent
>         INTO thisnode, thisparent
>         FROM cms.cms_items
>         WHERE itm_id = thisparent;
>     END LOOP;
>     RETURN thisnode;
> END;
> $BODY$
>   LANGUAGE 'plpgsql' STABLE
>   COST 100;
>
> This returns the ID of the root node for non-root nodes, the node's
> own ID for root-nodes and NULL for invalid IDs.
>
> I'm writing a query to do document searching (the version given is
> simplified to the problem in hand).
>
> SELECT cms_v_items.* ,
> getroot (cms_v_items.itm_id) AS itm_root
> FROM cms_v_items
> WHERE itm_root = ?;
>
> I was hoping this query would return a set of items that had the same
> root node.  Instead it throws an error, column itm_root does not
> exist.
>
> I'm obviously doing something wrong here, but what?
>
> --
> Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-general