Home > other >  SQL Selecting Values Based on Hierarchically-Ordered List
SQL Selecting Values Based on Hierarchically-Ordered List

Time:01-05

I have a table (at the bottom) with base values A1, A2, A3, A4 ... An, and overriding values B1, B3, and C2, C3, C4 and D1, D4.

The data could be visualized like this:

A1  A2  A3  A4
B1      B3 
C1  C2      C4
    D2  D3

With SQLite and the table below, if I have an ordered list of Letters A, B, C, and D in any order and length, I'd want the table to return those values contained in the highest level, and then fill in the missing holes from the lower levels.

Example: [D > B > A] (omitting C) should grab all the values 1-4 from "D": [D2, D3], then get missing values for 1 and 4 from B [B1] and finally the missin 4 from A [A4] to yield [B1, D2, D3, A4]. but [C > B > A] would return [C1, C2, B3, C4]. The order of the returne

Input          Output
C > B > A      [C1, C2, B3, C4]
B > A          [B1, A2, B3, A4]
A > B > C > D  [A1, A2, A3, A4]
D > C > A      [C1, D2, D3, C4]
B > D          [B1, D2, D3] or [B1, D2, D3, null]
B              [B1, B3] or [B1, null, N3, null]

So in my table I have my data arranged like this. In this table Number Letter is a composite key. Any combination of these rows will return either zero or one row. The rows are not guaranteed to be in any particular order: the example table is for ease of explanation. The results do not need to be in any certain order.

ID (PK) Number Letter Value
1 1 A a
2 2 A b
3 3 A c
4 4 A d
5 1 B e
6 3 B f
7 1 C g
8 2 C h
9 4 C i
10 2 D j
11 3 D k

CodePudding user response:

You can use row_number. Params CTE specifies the input hierarchy, C > B > A for example

with params(Letter, Pos) as (
  select 'A', 3 union all
  select 'B', 2 union all
  select 'C', 1 union all
  select 'D', -1 
)
select *
from(
 select t.*, row_number() over(partition by c.Number order by p.Pos) rn
 from mytable c
 join params p on p.Letter = c.Letter
   and p.Pos > 0
)t 
where rn = 1;

Returns

ID  Number  Letter  Value   rn
7   1   C   g   1
8   2   C   h   1
6   3   B   f   1
9   4   C   i   1

CodePudding user response:

Using exists with a subquery:

with inp(letter) as ( 
  select "D"
  union all
  select "B"
  union all
  select "A"
),
cte as (
  select row_number() over (order by 1) r, t1.number, t1.letter 
  from inp t join test t1 on t.letter = t1.letter
)
select c.number, c.letter from cte c where not exists (select 1 from cte c1 where c1.r < c.r and c.number = c1.number)
order by c.number
  •  Tags:  
  • Related