Select Distinct pair of column values - sql

I have a table like this
A1 | A2
a | b
c | d
b | a
a | b
And I want to select distinct pairs :
A1 | A2
a | b
c | d
I tried :
select a, b from (
select a, b , a|b as ab, b|a as ba from T
)t where ab!=ba group by a, b
Anyone have a better idea about how I can do this ?
Thanks

An ANSI compliant way of doing this would be to rearrange each pair of A1 and A2 values as min/max using CASE expressions. Then just select distinct on this derived table.
SELECT DISTINCT
A1, A2
FROM
(
SELECT
CASE WHEN A1 < A2 THEN A1 ELSE A2 END AS A1,
CASE WHEN A1 < A2 THEN A2 ELSE A1 END AS A2
FROM yourTable
) t

This would be the cleanest way if NULL values are not involved
select distinct
least (A1,A2) as A1
,greatest (A1,A2) as A2
from t
;
+-----+-----+
| a1 | a2 |
+-----+-----+
| a | b |
| c | d |
+-----+-----+

Related

count on one column and group by another column

Suppose that we have a sample table
c1 | c2
--------------
a | b
a | b
a | c
a | c
a | c
d | e
d | e
How can we turn this table into the following format
c1 | c2 | c3
--------------------------
a | b | 2
a | c | 3
d | e | 2
where c3 contains the count of c2 based on distinct c1 value.
This is a group by with two keys:
select c1, c2, count(*)
from t
group by c1, c2;
select c1, c2, count(*) as c3
from your_table
group by c1, c2

SQL query for many to many exclusive IN query

I have a table Table1 with columns A and B (many to many table).
|---------------------|------------------|
| ColumnA | ColumnB |
|---------------------|------------------|
| a1 | b1 |
|---------------------|------------------|
| a1 | b2 |
|---------------------|------------------|
| a2 | b1 |
|---------------------|------------------|
| a2 | b3 |
|---------------------|------------------|
| a3 | b2 |
|---------------------|------------------|
I want a list of As whose Bs are ONLY in list of Bs.
So, from above table, if list is [b1, b2]
Expected [a1, a3]
Not including a2as it is associated with b3 also.
You can use aggregation and having:
select a
from ab
group by a
having sum(case when b not in ('b1', 'b2') then 1 else 0 end) = 0;
The having clause is checking the number of rows that are not in the list. The = 0 says there are none.
Assuming there are not any nulls in ColumnB you can use NOT EXISTS:
select t.*
from tablename t
where not exists (select 1 from tablename where ColumnA = t.ColumnA and ColumnB not in ('b1', 'b2'))
If you want only the distinct values of ColumnA:
select distinct t.ColumnA
from tablename t
where not exists (select 1 from tablename where ColumnA = t.ColumnA and ColumnB not in ('b1', 'b2'))
See the demo.

Join on two tables gives duplicate results

i have a table with data that I want to join unto another table. Problem is that the join can happen on two columns of the same table, where I want to get the first join to work and if this Fails i want the second join to give me a valid result.
Base table:
| ID1 | ID2 | Value |
| a1 | a2 | val_1 |
| b1 | b2 | val_2 |
| c1 | c2 | val_3 |
join Table:
| ID1 | ID2 | Join_Value |
| | a2 | join_val_1 |
| b1 | | join_val_2 |
| c1 | c2 | join_val_3 |
What i tried was this:
select base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
The Result is this:
| ID1 | ID2 | Value | Join_Value |
| a1 | a2 | val_1 | join_val_1 |
| b1 | b2 | val_2 | join_val_2 |
| c1 | c2 | val_3 | join_val_3 |
| c1 | c2 | val_3 | join_val_3 |
What i want is this:
| ID1 | ID2 | Value | Join_Value |
| a1 | a2 | val_1 | join_val_1 |
| b1 | b2 | val_2 | join_val_2 |
| c1 | c2 | val_3 | join_val_3 |
I hope i made my Problem clear.
You don't need to join the same table twice. Just specify the condition in the ON
select b.ID1, b.ID2, b.[Value], j.Join_Value
from [base] b
inner join [join] j on b.ID1 = j.ID1
or (
j.ID1 = ''
and b.ID2 = j.ID2
)
You are going to get duplicate rows for for the c1 and c2 rows because they match on both of your Join table joins (j1 and j2).
A quick fix is to add a DISTINCT to your query:
select DISTINCT base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value
from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
A better fix, depending on your DBMS is to use a window function:
select id1, id2, Value, Join_Value
FROM (
select base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value,
ROW_NUMBER() OVER(
PARTITION BY base.id1, base.id2 -- Group rows based on (id1, id2) combination
ORDER BY j1.id1 -- If more than one row, give priority to row with "id1" value
) AS RowNum
from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
) src
WHERE RowNum = 1 -- Only return one row
This will make sure you always one row maximum per (id1, id2) combination.
Try:
select *
from base b
join [join] j on b.id1 = j.id1 or b.id2 = j.id2
First, your version does exactly what you want. Here is a db<>fiddle.
Second, for more control over the matching, you can use a lateral join. This allows you to choose only one matching row -- say the one where both ids match:
select b.id1, b.id2, b.value, jt.join_value
from base b cross apply
(select top (1) jt.*
from jointable jt
where b.id1 = jt.id1 or
b.id2 = jt.id2
order by (case when b.id1 = jt.id1 then 1 else 0 end) +
(case when b.id2 = jt.id2 then 1 else 0 end) desc
) jt ;

Oracle SQL: Exclude IDs from another table without subquery join

I would like to know if the following is possible without joining the same table twice:
Table A:
+----+------+
| ID | ColA |
+----+------+
| 1 | A1 |
| 2 | A2 |
| 3 | A3 |
| 4 | A4 |
+----+------+
Table B:
+----+------+
| ID | ColB |
+----+------+
| 1 | B1 |
| 2 | B2 |
| 3 | B3 |
| 4 | B4 |
| 5 | B5 |
| 6 | B6 |
+----+------+
Table C:
+----+
| ID |
+----+
| 1 |
| 2 |
+----+
Desired result: (A LEFT JOIN B WITHOUT C)
+----+------+------+
| ID | ColA | ColB |
+----+------+------+
| 3 | A3 | B3 |
| 4 | A4 | B4 |
+----+------+------+
So basically I need to add Column B to Table A, hence left join, and exclude all IDs which occur in Table C.
Current solution:
SELECT a.id, a.ColA, b.ColB
FROM tableA a
LEFT JOIN tableB b ON a.id = b.id
WHERE a.id NOT IN(
SELECT a2.id FROM tableA a2
LEFT JOIN tableC c on a2.id = c.id)
What's irritating me is, that the exclusion of table C requires an additional left join of table A with table C. Isn't there a more straight-forward approach, without having to join table A again as part of the subquery, if all I want to do is to exclude IDs which occur in table C from the resultset?.
Thanks
Use a not exists:
SELECT a.id, a.ColA, b.ColB
FROM tableA a
LEFT JOIN tableB b ON a.id = b.id
where not exists(select 1 from tablec c where a.id = c.id)
The issue with using a not in with a select in Oracle is that:
a) it has to return the whole subquery dataset
b) if there are nulls, it breaks
TOM link regarding these 2 issues
won't this work?
SELECT a.id, a.ColA, b.ColB
FROM tableA a
JOIN tableB b ON a.id = b.id
WHERE a.id NOT IN (SELECT c.Id FROM tableC c)
this can also be done in a join
SELECT a.id, a.ColA, b.ColB
FROM tableA a
JOIN tableB b ON a.id = b.id
LEFT JOIN tableC C ON a.id = c.id
WHERE c.Id is null

SQL Join 2 tables without a relation

Let's say I have the following tables:
Table1 with cols: A, B
Table2 with col: C
And I have a variable #d.
|Table1| |Table2| #d = 5;
------ ------
|A | B| | C |
------- -------
a1 | b1 c1
a2 | b2 c2
How can I display following output?
| ResultTable |
------------------
|A | B | C | d|
a1 b1 c1 5
a2 b2 c2 5
PS: I am using T-SQL.
You can use row_number to give each row a number and join on that. This assumes both tables have the same # of rows.
select *, #d from (
select *, row_number() over (order by A) rn
from Table1
) t1 join (
select *, row_number() over (order by C) rn
from Table2
) t2 on t1.rn = t2.rn