Обсуждение: PL/pgSQL graph enumeration function hangs

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

PL/pgSQL graph enumeration function hangs

От
"Charles F. Munat"
Дата:
I have a table of organizations that has a many-to-many relationship
with itself via another table called relationships. The relationships
table has a serial id primary key and parent_id and child_id integer
fields. The organizations table has a couple thousand records and the
maximum depth is around 7 or 8 levels. The graph is directed and is (or
will be) reconvergent.

Using pseudocode from Celko's "SQL for Smarties" book, I wrote the
following function that builds a path enumeration table. I hope to
trigger this function on the rare occasions that the organizations table
is updated. But when I run this function, it hangs.

Can anyone spot the problem? If not, is there a better solution?

I am returning a trigger. There are no arguments. I'm using VOLATILE,
CALLED ON NULL INPUT, and SECURITY INVOKER.

-------------------------------------------------------------------------

DECLARE
   oldsize integer NOT NULL;
   newsize integer NOT NULL;

BEGIN

   -- recreate the empty path_enum table
   DROP TABLE IF EXISTS organizations_path_enum;
   CREATE TABLE organizations_path_enum (
       parent_id integer NOT NULL,
       child_id integer NOT NULL,
       depth integer NOT NULL,
       CONSTRAINT depth_not_negative CHECK( depth >= 0 )
   );
   CREATE INDEX ope_parent_idx ON organizations_path_enum(parent_id);
   CREATE INDEX ope_child_idx ON organizations_path_enum(child_id);
   CREATE INDEX ope_parent_child_idx ON
organizations_path_enum(parent_id, child_id);
   CREATE INDEX ope_child_parent_idx ON
organizations_path_enum(child_id, parent_id);
   CREATE UNIQUE INDEX ope_uniq_row_idx ON organizations_path_enum
(parent_id, child_id, depth);

   -- load path of node to itself
   INSERT INTO organizations_path_enum
       SELECT DISTINCT child_id, child_id, 0 FROM relationships;

   -- load paths of length = 1 into table
   INSERT INTO organizations_path_enum
       SELECT DISTINCT parent_id, child_id, 1 FROM relationships;

   -- insert rows only while table grows
   oldsize := 0;
   SELECT COUNT(*) INTO newsize FROM organizations_path_enum;

   WHILE oldsize < newsize LOOP
       INSERT INTO organizations_path_enum
           SELECT o1.parent_id, r1.child_id, (o1.depth + 1)
           FROM organizations_path_enum o1, relationships r1
               -- advance existing paths by one level
           WHERE EXISTS (SELECT * FROM organizations_path_enum AS o2
WHERE r1.parent_id = o2.child_id)
               -- insert only new rows into the table
           AND NOT EXISTS (SELECT * FROM organizations_path_enum AS o3
WHERE o1.parent_id = o3.parent_id AND r1.child_id = o3.child_id);

       oldsize := newsize;
       SELECT COUNT(*) INTO newsize FROM organizations_path_enum;
   END LOOP;
END;

-------------------------------------------------------------------------

Thanks!

Charles Munat
Seattle


Re: PL/pgSQL graph enumeration function hangs

От
Tom Lane
Дата:
"Charles F. Munat" <chas@munat.com> writes:
> Using pseudocode from Celko's "SQL for Smarties" book, I wrote the
> following function that builds a path enumeration table. I hope to
> trigger this function on the rare occasions that the organizations table
> is updated. But when I run this function, it hangs.

I think there might be something wrong with this query:

>        INSERT INTO organizations_path_enum
>            SELECT o1.parent_id, r1.child_id, (o1.depth + 1)
>            FROM organizations_path_enum o1, relationships r1
>                -- advance existing paths by one level
>            WHERE EXISTS (SELECT * FROM organizations_path_enum AS o2
> WHERE r1.parent_id = o2.child_id)
>                -- insert only new rows into the table
>            AND NOT EXISTS (SELECT * FROM organizations_path_enum AS o3
> WHERE o1.parent_id = o3.parent_id AND r1.child_id = o3.child_id);

I'm not totally clear on what this is supposed to accomplish, but
it seems like there should be some join clause between o1 and r1.

            regards, tom lane

Re: PL/pgSQL graph enumeration function hangs

От
"Charles F. Munat"
Дата:
Thanks, but the join clause is there, it's just buried in the subqueries.

If there is a problem, it is probably that the loop never ends.

Or it could be that the answer is exponential, and I just have too many
rows in the source table and too deep a graph.

I figured out how to do it in the application with one call to the
database and a simple recursive method in a class, though, so I'm not
going to use a stored function in the DB.

Thanks again for responding.

Chas.

Tom Lane wrote:
> "Charles F. Munat" <chas@munat.com> writes:
>> Using pseudocode from Celko's "SQL for Smarties" book, I wrote the
>> following function that builds a path enumeration table. I hope to
>> trigger this function on the rare occasions that the organizations table
>> is updated. But when I run this function, it hangs.
>
> I think there might be something wrong with this query:
>
>>        INSERT INTO organizations_path_enum
>>            SELECT o1.parent_id, r1.child_id, (o1.depth + 1)
>>            FROM organizations_path_enum o1, relationships r1
>>                -- advance existing paths by one level
>>            WHERE EXISTS (SELECT * FROM organizations_path_enum AS o2
>> WHERE r1.parent_id = o2.child_id)
>>                -- insert only new rows into the table
>>            AND NOT EXISTS (SELECT * FROM organizations_path_enum AS o3
>> WHERE o1.parent_id = o3.parent_id AND r1.child_id = o3.child_id);
>
> I'm not totally clear on what this is supposed to accomplish, but
> it seems like there should be some join clause between o1 and r1.
>
>             regards, tom lane

Re: PL/pgSQL graph enumeration function hangs

От
Tino Wildenhain
Дата:
Charles F. Munat wrote:
> Thanks, but the join clause is there, it's just buried in the subqueries.
>
> If there is a problem, it is probably that the loop never ends.
>
> Or it could be that the answer is exponential, and I just have too many
> rows in the source table and too deep a graph.
>
> I figured out how to do it in the application with one call to the
> database and a simple recursive method in a class, though, so I'm not
> going to use a stored function in the DB.

If you have figured it out this way you can even use one of the
PL/languages to implement it within the database :-)

Cheers
Tino

Вложения