Search code examples
sqliterecursionsql-order-by

Sqlite recursive ORDER BY


I have a table like this:

CREATE TABLE "tester" (
    "id"    INTEGER UNIQUE,
    "pid"   INTEGER,
    "pos"   INTEGER,
    "nm"    VARCHAR(255),
    PRIMARY KEY("id" AUTOINCREMENT)
);

INSERT INTO 'tester' (id, pid, pos, nm)
VALUES (1,  0, 0, "My Tester"),
       (2, 1, 0, "cat1"),
       (4, 9, 1, "subcat1"),
       (9, 1, 1, "cat2"),
       (10, 2, 0, "subcat3"),
       (13, 2, 1, "subcat2"),
       (14, 9, 0, "subcat4"),
       (15, 4, 0, "data1"),
       (16, 4, 1, "data2"),
       (17, 13, 0, "data3"),
       (18, 13, 1, "data4"),
       (19, 10, 0, "data5"),
       (20, 10, 1, "data6"),
       (21, 14, 0, "data7"),
       (22, 14, 1, "data8");

This represents a tree like this:

Tree Look Like This

I want to run query to get all from "data1" till "data8" ordered by pos.

I have the ids of all nodes from "data1" till "data8" but they are not in order.

I tried :

SELECT * 
FROM "tester" 
WHERE id IN ("15,16,17,18,19,20,21,22") 
ORDER BY pos ASC

But the result is not as expected because the parent's positions are different.

The expected result is:

data5
data6
data3
data4
data7
data8
data1
data2

Solution

  • You can use a recursive common table expression to visit all records by following parent-child relationships. Use order by to make sure that a node's depth is given priority, or when nodes have the same depth, that pos defines the order of visiting them.

    From the CTE you can then select what you need, filtering records by the desired id and selecting the names:

    with recursive depth_first(id, pos, nm, level) as (
    
        select   id, pos, nm, 0
        from     "tester"
        where    pid = 0
    
        union all
    
        select   t.id, t.pos, t.nm, depth_first.level+1
        from     "tester" t
        join     depth_first 
              on t.pid = depth_first.id
        order by 4 desc, 2
    )
    select nm 
    from   depth_first
    where  id between 15 and 22;