To solve your problem in a compact way, I converted your device_from
and device_to
columns to numbers (integers)
CREATE TABLE wiretable (
wire text,
device_from integer,
device_to integer,
level text);
INSERT INTO wiretable
(wire, device_from, device_to, level)
VALUES('Wire 1', 1, 2, 'level 1'),
('Wire 2', 1, 2, 'level 1'),
('Wire 3', 2, 1, 'level 1');
You need a way of comparing Device 1 and Device 2 so that 1,2
is the same as 2,1
. The only way I could think of to do it was by using an array and sorting. But to do array sorting, you have to install the intarray extension (CREATE EXTENSION intarray;
) or do some tricks. See this question for how to sort arrays and/or install extension.
Basically you want to do a GROUP BY on two columns. I couldn't find anything like that in the documentation. So I put both columns in an array and GROUP BY that array. I have to do it in two steps.
SELECT count(*), string_agg(wire, ', '),
sort(array[device_from, device_to]) as combo,
level
FROM wiretable
GROUP BY combo, level;
count |
string_agg |
combo |
level |
3 |
Wire 1, Wire 2, Wire 3 |
{1,2} |
level 1 |
That looks pretty good, but now we have to split combo
back into two columns. We do this with a CTE
WITH foo AS (SELECT count(*), string_agg(wire, ', '),
sort(array[device_from, device_to]) as combo,
level
FROM wiretable
GROUP BY combo, level)
SELECT foo.count, foo.string_agg as wires,
foo.combo[1] as "Device From",
foo.combo[2] as "Device To",
foo.level
FROM foo
which gives
count |
wires |
Device From |
Device To |
level |
3 |
Wire 1, Wire 2, Wire 3 |
1 |
2 |
level 1 |