Home > Software design >  How to select and count pairs or triplets from 5 columns in PostgreSQL
How to select and count pairs or triplets from 5 columns in PostgreSQL

Time:12-29

I have a postgreSQL table where i store in five different columns (say col1, col2, col3, col4, col5) numbers. In each row the numbers stored are different from each other.

I want to make some select that gives me the pairs that exists and how many times they appear (count the times that they are present in rows).

Example:

col1 col2 col3 col4 col5
1 5 10 20 100
5 20 30 40 100

Results (something more or less like this):

pair total
1,5 1
1,10 1
1,20 1
1,100 1
5,10 1
5,20 2
5,100 2
10,20 1
10,100 1
20,100 2
5,30 1
5,40 1
20,30 1
20,40 1

I can make some SQL to execute code in specific columns:

SELECT count (*) as total, col1, col2
FROM numbers
group by col1, col2;

But that won't give me all the combinations and I don't know how to get the rest of the pairs. Also, this may be inefficient in terms of performance.

Any help will be appreciated.

Regards, Miguel.

CodePudding user response:

You can use recursive query for generating the output you are expecting

Demo

-- You can change "select 3" to "select 2" or any number you want

with recursive col_join as (select 3),
               numbers_row as (
                   select *, row_number() over () as row
                   from numbers
               ),
               cte_r as (
                   select col1 as value, row, 1 as col
                   from numbers_row
                   union all
                   select col2 as value, row, 2 as col
                   from numbers_row
                   union all
                   select col3 as value, row, 3 as col
                   from numbers_row
                   union all
                   select col4 as value, row, 4 as col
                   from numbers_row
                   union all
                   select col5 as value, row, 5 as col
                   from numbers_row
               ),
               cte as (
                   select array_agg(value order by col) as value,
                          row,
                          col
                   from cte_r
                   group by row, col
                   union all
                   select c.value || cr1.value as value,
                          cr1.row,
                          cr1.col
                   from cte c,
                        cte_r cr1
                   where c.row = cr1.row
                     and not c.value @> array [cr1.value]
                     and c.col < cr1.col
                     and array_length(c.value || cr1.value, 1) <= (select * from col_join)
               )
select array_to_string(value, ','), count(*)
from cte
where array_length(value, 1) = (select * from col_join)
group by 1
order by 1

**Old scenario for joining two-column"

Demo

with recursive 
  numbers_row as (
    select *, row_number() over () as row from numbers
  ),
  cte_r as (
    select col1 as value, row, 1 as col from numbers_row
    union all
    select col2 as value, row, 2 as col from numbers_row
    union all
    select col3 as value, row, 3 as col from numbers_row
    union all
    select col4 as value, row, 4 as col from numbers_row
    union all
    select col5 as value, row, 5 as col from numbers_row
  ),
  cte as (
    select 
      value as val1, 
      value as val2, 
      row, 
      col
    from cte_r
    union all
    select 
      c.val1 as val1,
      cr.value as val2,
      cr.row,
      cr.col
    from 
      cte c,
      cte_r cr 
    where c.row = cr.row and c.col = cr.col - 1
  )
select val1 || ',' || val2, count(*)
from cte
where val1 <> val2
group by val1, val2, val1 || ',' || val2
order by val1, val2
  • Related