Show distinct tuples regardless of column order

2020-07-30 01:34发布

问题:

Say I have following results

----------------------
|   col1  |   col2   |
----------------------
|    a    |    b     |
|    b    |    a     |
|    c    |    d     |
|    e    |    f     |
----------------------

I would like to get distinct tuple regardless of column order. In other words, (a, b) and (b, a) are considered "same" because changing the order make one same as the other (a, b) == (a, b). So, after executing query should be:

----------------------
|   col1  |   col2   |
----------------------
|    a    |    b     | // or (b, a)
|    c    |    d     |
|    e    |    f     |
----------------------

Can any query expert help me on this? I've been stuck for few hours and wasn't able to solve this.

Below is my detailed scenario I'm working on.

I have the following relations:

Ships(name, country) // ("Lincoln", "USA") = "Ship Lincoln belongs to USA"
Battles(ship, battleName) // ("Lincoln", "WW2") = "Ship Lincoln fought in WW2"

And I need to find: List all pairs of countries that fought each other in battles

I was able to find all pairs by executing below query:

 SELECT DISTINCT c1, c2
 FROM
 (SELECT DISTINCT s1.country as c1, battleName as b1
  FROM Ships as s1, Battles
  WHERE s1.name = ship) as t1
 JOIN
 (SELECT DISTINCT s2.country as c2, battleName as b2
  FROM Ships as s2, Battles
  WHERE s2.name = ship) as t2
 ON (b1 = b2)
 WHERE c1 <> c2

And the result of executing above query is:

---------------------------------
|       c1      |       c2      |
---------------------------------
|       USA     |     Japan     |   // Row_1
|      Japan    |      USA      |   // Row_2
|     Germany   | Great Britain |   // Row_3
| Great Britain |    Germany    |   // Row_4
---------------------------------

But Row_1 and Row_2 are same as well as Row_3 and Row_4.

What I need is to print either one of Row_1 or Row_2 and either Row_3 or Row_4.

Thank you

回答1:

Try it this way

SELECT DISTINCT
       LEAST(s1.country, s2.country) c1,
       GREATEST(s1.country, s2.country) c2
  FROM battles b1 JOIN battles b2
    ON b1.battlename = b2.battlename
   AND b1.ship <> b2.ship JOIN ships s1
    ON b1.ship = s1.name JOIN ships s2
    ON b2.ship = s2.name
HAVING c1 <> c2

Output:

|      C1 |            C2 |
|---------|---------------|
| Germany | Great Britain |
|   Japan |           USA |

Here is SQLFiddle demo



回答2:

Here is how you can do it

Sample data

| COL1 | COL2 |
|------|------|
|    a |    b |
|    b |    a |
|    c |    d |
|    e |    f |

Query

SELECT
  k.*
FROM test k
  LEFT JOIN (SELECT
               t.col1
             FROM test t
               INNER JOIN test r
                 ON (r.col1 = t.col2
                     AND t.col1 = r.col2)
             LIMIT 1) b
    ON b.col1 = k.col1
WHERE b.col1 IS NULL

OUTPUT

| COL1 | COL2 |
|------|------|
|    a |    b |
|    c |    d |
|    e |    f |

SQL Fiddle Demo



回答3:

That's an interesting question, looks simple but is tricky. I have tried it on SQL Server. Here is my query, assuming input table 'test' contains distinct rows:

| COL1 | COL2 |
|------|------|
|    a |    b |
|    b |    a |
|    c |    d |
|    a |    e |

SELECT t1.col1, t1.col2
FROM test t1
EXCEPT
SELECT t1.col1, t1.col2
FROM test t1
INNER JOIN test t2
ON t1.col1 = t2.col2 AND t1.col2 = t2.col1
AND t1.col1 > t1.col2  

Please replace it with analogous MySQL query if it does not work verbatim. Let me know if this worked for you.