I have a products table which contains all my products. Those products table gets filled permanently with new products. However, I want to have the possibility to “hold up”/”pin” certain products to a place in the returned query collection.
Means, I want to set something like rank_index
which contains the number the product should have in the returned query collection.
Example:
id title rank_index
1 An awesome product
2 Another product 5
3 Baby car
4 Green carpet 2
5 Toy
Lets assume the default order would be the id
. But because the rank_index
is set for the product with the id
4 I would like to get the collection with the following order of ids returned: 1, 4, 3, 5, 2
.
Is this somehow possible to do? The rank_index
column was just an idea of mine. I mean.. I also could do this on the php
side and do a normal query which does only include the products
without an rank_index
and one which only contains products
with an index_rank
and order them manually on the php
side.
However, because this takes a lot of time and processing power I am looking for a solution which is done by the database… Any ideas?
Btw: I am using Laravel 8 if this makes any difference.
Kind regards
Advertisement
Answer
This is a very tricky problem. If you try the other approach setting consecutive values — like 2 and 3 — you will see that they do not work.
There may be simpler ways to solve this. But, here is a brute force approach.
- It constructs a derived table by enumerating the rows in the original table.
- It adds into this table (using a
left join
) all the force-ranked values. - It joins in the rest of the values by enumerating the empty slots both in
table1
and in the derived table.
So:
with recursive n as (
select row_number() over (order by id) as n
from table1 t1
),
nid as (
select n.n, t1.id
from n left join
table1 t1
on t1.rank_index = n.n
),
nids as (
select n.n, coalesce(n.id, t1.id) as id
from (select nid.*, sum(nid.id is null) over (order by nid.n) as seqnum
from nid
) n left join
(select t1.*, row_number() over (order by id) as seqnum
from table1 t1
where rank_index is null
) t1
on n.seqnum = t1.seqnum
)
select t1.*
from nids join
table1 t1
on t1.id = nids.id
order by nids.n;