Question

I have to handle a table PRODUCTS which is created to accommodate tree structure of products. It is done to handle situations when one product can contain several others (e.g. one package product holds several other positions). So, I'm making a function that takes OrderDetails, and it must iterate through all PRODUCTS and list out the child products for each product listed. I am facing an issue that I have to iterate through tree of unknown depth. Please, give me an idea how to do it.

I've implemented it in the table below with the function listed along with it. But in that solution the depth of listing is limited to 1, and what i want to do is to fetch all depth of the tree.

Here is the code:

CREATE OR REPLACE FUNCTION foo()RETURNS text AS 
$body$
DECLARE _row RECORD;
        _result text := '';
        _child_row RECORD;
        _count integer := -1;
         _marker integer := 1;
BEGIN
    FOR _row IN SELECT * FROM tree_products
    LOOP
        _result := _result || _marker || ' ' || _row.name;
        _count := (SELECT count(product_id) FROM tree_products WHERE parent_id = _row.product_id);
        IF _count > 0 THEN
             FOR _child_row IN SELECT * FROM tree_products WHERE parent_id = _row.product_id
            LOOP
                _result := _result || ' ' || _child_row.name;
            END LOOP;
         END IF;
         _marker := _marker =1;      
    END LOOP;
END;
$body$
    LANGUAGE plpgsql 

UPD Done this usign WITH CTE, but the groupiing problem occured:

CREATE OR REPLACE FUNCTION public.__foo (
)
RETURNS SETOF refcursor AS
$body$
DECLARE _returnvalue refcursor;
        _q text;
 BEGIN
_q :='
        WITH RECURSIVE r_p (product_id, name, parent_id) AS    -- 1
    (SELECT t_p.product_id, t_p.name , t_p.parent_id -- 2
   FROM   tree_products t_p
   WHERE  t_p.product_id = 1
   UNION ALL
   SELECT t_c.product_id, t_c.name, t_c.parent_id    -- 3
   FROM   r_p t_p, tree_products t_c
   WHERE  t_c.parent_id = t_p.product_id)
SELECT product_id, name, parent_id                       -- 4
FROM   r_p;';
OPEN _returnvalue FOR EXECUTE (_q);
RETURN NEXT _returnvalue;
END
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100 ROWS 1000;

I want to sibling products be under their respectiveparents, I wonder how to write Grouping statement...

UPD Sorry, the definition of the tree_products is the following:

CREATE TABLE public.tree_products (
  product_id INTEGER DEFAULT nextval('ree_products_product_id_seq'::regclass) NOT NULL,
  name VARCHAR,
  parent_id INTEGER,
  CONSTRAINT ree_products_pkey PRIMARY KEY(product_id)
) 
WITH (oids = false);

UPD: SAMPLE OUTPUT:

product_id | name          | parent_id
---------------------------------------
1          | promo         | NULL
3          | fork          | 1
4          | spoon         | 1
6          | can           | 1
10         | big can       | 3
11         | small can     | 4
12         | large spoon   | 6
13         | mega fork     | 3
14         | super duper   | 6

DESIRED OUTPUT:

product_id | name          | parent_id
---------------------------------------
1          | promo         | NULL
3          | fork          | 1
10         | big can       | 3
13         | mega fork     | 3
4          | spoon         | 1
11         | small can     | 4
6          | can           | 1
12         | large spoon   | 6
14         | super duper   | 6

So, the fetched table has structure of the real tree, like the follwing:
 - promo
   - fork
     - big can
     - mega fork
   - spoon
     - small can
   - can
     - large can
     - super duper
Was it helpful?

Solution

This SQLFiddle traverses the tree top-down, keeping an list of parent row numbers in an array, essentially a "parent row position list".

It then sorts the results by the parent-list.

WITH RECURSIVE tree(product_id, name, parentlist) AS (
  SELECT product_id, name, ARRAY[ row_number() OVER (ORDER BY product_id) ]
  FROM tree_products
  WHERE parent_id IS NULL
  UNION
  SELECT tp.product_id, tp.name, array_append(parentlist, row_number() OVER (ORDER BY tp.product_id))
  FROM tree_products tp
  INNER JOIN tree t
  ON (tp.parent_id = t.product_id)
)
SELECT *
FROM tree
ORDER BY parentlist;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top