1

I have this table named items with following columns and rows.

id   item_name   parentId
--------------------------
1    Item 1         0
2    Item 2         1
3    Item 3         2
4    Item 4         1
5    Item 5         2
6    Item 6         3
7    Item 7         6
8    Item 8         7

I could measure the level of rows parent by recursive CTE, but I still need to get the path as this format: 1.1.1, 1.2.1 ... This is my current query:

with recursive items_cache (lvl, id, item_name, parent_id) as (
    select 0 as lvl, id, item_name, parent_id
    from items
    where parent_id = 0
  union all
    select items_cache.lvl + 1, items.id, items.item_name, items.parent_id
    from items_cache    
    join items  on items_cache.id = items.parent_id
    order by items_cache.lvl+1 desc, items.id
)
select
    lvl,
    *
from items_cache

Desired results:

id   item_name   parentId   lvl       path_index
-------------------------------------------------
1    Item 1         0        0        1
2    Item 2         1        1        1.1
3    Item 3         2        2        1.1.1
6    Item 6         3        3        1.1.1.1
7    Item 7         6        4        1.1.1.1.1
8    Item 8         7        5        1.1.1.1.1.1
5    Item 5         2        2        1.1.2
4    Item 4         1        1        1.2
9    Item 9         0        0        2

How can I do that in SQLite? The SQLite version that android runs is 3.21, so it does not support window functions.

2 Answers 2

1

Use a recursive cte to get the level of each id, then ROW_NUMBER() window function to get the rank of each id under its parent and another recursive cte that will conatenate the row numbers:

WITH 
  levels AS (
    SELECT *, 0 lvl FROM items
    UNION ALL
    SELECT i.*, l.lvl + 1 
    FROM items i INNER JOIN levels l
    ON l.id = i.parentId 
  ),
  row_numbers AS (
    SELECT id, item_name, parentId, MAX(lvl) lvl,
           ROW_NUMBER() OVER (PARTITION BY parentId, lvl ORDER BY id) rn 
    FROM levels
    GROUP BY id, item_name, parentId
  ),
  cte AS (
    SELECT id, item_name, parentId, lvl, rn || '' path_index
    FROM row_numbers
    UNION ALL
    SELECT r.id, r.item_name, r.parentId, r.lvl, 
           c.path_index || '.' || r.rn
    FROM row_numbers r INNER JOIN cte c
    ON r.parentId = c.id 
  )
SELECT * 
FROM cte  
GROUP BY id
HAVING MAX(LENGTH(path_index))
ORDER BY path_index

See the demo.

If your version of SQLite does no support window functions use:

(SELECT COUNT(*) FROM levels l2 
 WHERE l2.parentId = l1.parentId AND l2.lvl = l1.lvl AND l2.id <= l1.id) rn

instead of:

ROW_NUMBER() OVER (PARTITION BY parentId, lvl ORDER BY id) rn

See the demo.

Results:

id item_name parentId lvl path_index
1 Item 1 0 0 1
2 Item 2 1 1 1.1
3 Item 3 2 2 1.1.1
6 Item 6 3 3 1.1.1.1
7 Item 7 6 4 1.1.1.1.1
8 Item 8 7 5 1.1.1.1.1.1
5 Item 5 2 2 1.1.2
4 Item 4 1 1 1.2
9 Item 9 0 0 2
Sign up to request clarification or add additional context in comments.

10 Comments

I'm getting error when I try to run in android SQL, the error says: near "(": syntax error: WITH levels AS ( SELECT , 0 lvl FROM items UNION ALL SELECT i., l.lvl + 1 FROM items i INNER JOIN levels l
@alexandre9865 The version of SQLite that you use in Android is old and does not support ctes and window functions.
@alexandre9865 ctes are supported since version 3.8.3 and window functions since version 3.25.0. Check your version.
@alexandre9865 then try this: dbfiddle.uk/…, but it will not be as fast as the query with ROW_NUMBER().
@alexandre9865 check this: dbfiddle.uk/…
|
1

You can use a subquery to compute the path index for each entry at a given iteration, and then use string concatenation with another recursive CTE:

with recursive to_r as (
   select row_number() over (order by i.id) r, i.* from items i
),
cte(id, name, parent, lvl, ind) as (
   select i.id, i.item_name, i.parentId, 0, (select sum(i1.parentId = i.parentId and i1.r < i.r) from to_r i1) + 1 from to_r i where i.parentId = 0
   union all
   select i1.id, i1.item_name, i1.parentId, i.lvl+1, (select sum(i2.parentId = i1.parentId and i2.r < i1.r) from to_r i2) + 1 from cte i join to_r i1 on i1.parentId = i.id
   
),
cte1(id, name, parent, lvl, ind, lvl1) as (
   select i.id, i.name, i.parent, i.lvl, case when i.lvl != 0 then "1." || i.ind else i.ind end, case when i.lvl != 0 then i.lvl-1 else i.lvl end from cte i
   union all
   select i.id, i.name, i.parent, i.lvl, "1." || i.ind, i.lvl1-1 from cte1 i where i.lvl1 > 0
)
select id, name, parent, lvl, ind from cte1 where lvl1 = 0 order by ind;

3 Comments

I'm getting error when I try to run in android SQL, the error says: near "(": syntax error: with recursive to_r as ( select row_number() over (
@alexandre9865 As forpas mentioned, you might need to check your version of SQLite.
In fact, I have no support for window functions

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.