Skip to content
Advertisement

Unique combination of records in a SQL table (Get edges from nodes table)

I came across a use case where I wanted to generate a combination table where the attributes of a record had to be matched with all other records in the table. Something like creating list of edges in a Complete Graph using a nodes table.

enter image description here

To give you an example of the requirement, you can consider the following table (Postgres).

create table test_users
(
    id         uuid default uuid_generate_v4(),
    first_name text,
    last_name  text
);

I added some records into the table using the following insert queries

INSERT INTO public.test_users (id, first_name, last_name) VALUES ('3ef309db-07cd-4e3f-95db-406f8165e54d', 'john', 'doe');
INSERT INTO public.test_users (id, first_name, last_name) VALUES ('50f33b51-1d3a-463c-9d5a-6f0b2c15cba2', 'marry', 'poppins');
INSERT INTO public.test_users (id, first_name, last_name) VALUES ('9812fbcf-d269-47a4-801d-2a8916e114e1', 'tony', 'stark');
INSERT INTO public.test_users (id, first_name, last_name) VALUES ('8d7b8d00-9602-4a12-914d-3df6a40d2716', 'idris', 'alba');
INSERT INTO public.test_users (id, first_name, last_name) VALUES ('2dee874f-f41e-4d68-a341-45e0a18079df', 'john', 'frusciante');

And you’ll get the following table in place

                  id                  | first_name | last_name
--------------------------------------+------------+------------
 3ef309db-07cd-4e3f-95db-406f8165e54d | john       | doe
 50f33b51-1d3a-463c-9d5a-6f0b2c15cba2 | marry      | poppins
 9812fbcf-d269-47a4-801d-2a8916e114e1 | tony       | stark
 8d7b8d00-9602-4a12-914d-3df6a40d2716 | idris      | alba
 2dee874f-f41e-4d68-a341-45e0a18079df | john       | frusciante

Now I want to create a combination of these names in such a way that the first_name of a row should have all the possible combination available such that (f1, f2) = (f2, f1). To give you some more context the expected output should be as follows.

 first_name | first_name | last_name  | last_name
------------+------------+------------+------------
 john       | marry      | doe        | poppins
 john       | tony       | doe        | stark
 john       | idris      | doe        | alba
 john       | john       | doe        | frusciante
 marry      | tony       | poppins    | stark
 marry      | idris      | poppins    | alba
 marry      | john       | poppins    | frusciante
 tony       | idris      | stark      | alba
 tony       | john       | stark      | frusciante
 idris      | john       | alba       | frusciante

You can see that the combination of column 1 and column 2 for first_name is unique in the table.

The closest that I was able to reach was to have an inner join using the following query.

select t1.first_name,
    t2.first_name,
    t1.last_name,
    t2.last_name
from test_users t1
        left join test_users t2 on t1.id <> t2.id;

But this yields all the records where (f1, f2) != (f2, f1) hence creating redundant records. Is there a way to get unique list of edges from the list of nodes in a Complete Graph using SQL?

Advertisement

Answer

Just use < (or >) instead of <>in the join condition:

select 
    t1.first_name first_name1, 
    t2.first_name first_name2, 
    t1.last_name last_name1, 
    t2.last_name last_name2
from test_users t1
inner join test_users t2 on t1.id < t2.id

Notes:

  • you probably want to alias the column in the resultset to disambiguate the columns coming from both tables

  • no need for a left join – an inner join does what you want

9 People found this is helpful
Advertisement