let union show values beside each other instead of below each other - sql

I got a SQL query which displays values out of two tables below each other. Now I want the table to select the values out of the two different tables besides each other. This is my query right now:
(select i1,i2 from t1
except
select i1,i2 from t2)
union all
(select i1,i2 from t2
except
select i1,i2 from t1)
This is my sample data:
Data I have in table1:
i1 i2
---------
| 1 | 1 |
---------
| 2 | 2 |
---------
| 2 | 3 |
---------
data i have in table2:
i1 i2
---------
| 1 | 1 |
---------
| 2 | 3 |
---------
| 2 | 4 |
---------
outcome:
---------
| 2 | 2 | << this one comes from table 1
---------
| 2 | 4 | << this one comes from table 2
---------
wanted outcome:
t1 t2
-----------------
| 2 | 2 | 2 | 4 |
-----------------

I think the best way would be to use a FULL JOIN and then perform a WHERE with the NULL data. Because if you make a CROSS JOIN you would get more data than you need.
SELECT *
FROM t1
FULL JOIN t2
ON t1.i1 = 2.i1
AND t1.i2 = t2.i2
WHERE t1.Id IS NULL
OR t2.Id IS NULL
I'll explain with an example:
IF OBJECT_ID('tempdb..#t1') IS NOT NULL
DROP TABLE #t1
CREATE TABLE #t1
(
Id INT IDENTITY,
i1 INT,
i2 INT
)
INSERT INTO #t1
(
i1,i2
)
VALUES
(1,1)
,(2,2)
,(2,3)
,(2,6)
SELECT * FROM #t1
IF OBJECT_ID('tempdb..#t2') IS NOT NULL
DROP TABLE #t2
CREATE TABLE #t2
(
Id INT IDENTITY,
i1 INT,
i2 INT
)
INSERT INTO #t2
(
i1,i2
)
VALUES
(1,1)
,(2,3)
,(2,4)
,(2,5)
,(2,7)
SELECT * FROM #t2
SELECT *
FROM #t1
FULL JOIN #t2
ON #t1.i1 = #t2.i1
AND #t1.i2 = #t2.i2
WHERE #t1.Id IS NULL
OR #t2.Id IS NULL
SELECT *
FROM #t1 a
CROSS JOIN #t2 b
WHERE NOT EXISTS (SELECT 1
FROM #t2 c
WHERE a.i1 = c.i1
AND a.i2 = c.i2
)
AND NOT EXISTS (SELECT 1
FROM #t1 c
WHERE b.i1 = c.i1
AND b.i2 = c.i2
)
RESULT
In the first case you would get 5 records because (2,2) and (2,6) do not exist in t2, and (2,4), (2,5), (2,7) do not exist in t1. So you would have 5 results.
Id i1 i2 Id i1 i2
----------- ----------- ----------- ----------- ----------- -----------
2 2 2 NULL NULL NULL
4 2 6 NULL NULL NULL
NULL NULL NULL 3 2 4
NULL NULL NULL 4 2 5
NULL NULL NULL 5 2 7
(5 row(s) affected)
However, in the CROSS JOIN you would get 6 results because you would make a Cartesian product. 2 x 3 = 6 CROSS JOIN Explanation
Id i1 i2 Id i1 i2
----------- ----------- ----------- ----------- ----------- -----------
2 2 2 3 2 4
2 2 2 4 2 5
2 2 2 5 2 7
4 2 6 3 2 4
4 2 6 4 2 5
4 2 6 5 2 7
(6 row(s) affected)

You can do this with a CROSS JOIN and NOT EXISTS:
SELECT *
FROM t1 a
CROSS JOIN t2 b
WHERE NOT EXISTS (SELECT 1
FROM t2 c
WHERE a.i1 = c.i1
AND a.i2 = c.i2
)
AND NOT EXISTS (SELECT 1
FROM t1 c
WHERE b.i1 = c.i1
AND b.i2 = c.i2
)
A CROSS JOIN joins every record from one table with every record from the other, so every combination of rows is returned. NOT EXISTS is used to filter out records from t1 that ever appear in t2 and vice versa for the 2nd NOT EXISTS

try
SELECT *
FROM
(
SELECT i1, i2
FROM t1
EXCEPT
SELECT i1, i2
FROM t2
) a,
(
SELECT i1, i2
FROM t2
EXCEPT
SELECT i1, i2
FROM t1
) b

Related

query select equaled data on three columns of table with four columns and ignore one

I have table have four columns like (right ,left ,up ,down) I want to build query that show equaled data on three of the four columns;
example:
| id | right | left | up | down |
|:---|:------:|:-----:|:------:| -----:|
| 1 | street |hospital|coffee |building|
| 2 | house |hospital|coffee |building|
| 3 | road | bus |coffee |sign |
| 4 | house |hospital|coffee |sign |
| 5 | car |road |coffee |sign |
the result should be like:
id
right
left
up
down
1
street
hospital
coffee
building
2
house
hospital
coffee
building
id number 3 and 5 not included because every column should equal it's self
is that query correct
select t.* from test_table t where
(t.right,t.left,t.up) in (select t.right,t.left,t.up from test_table t group by t.right,t.left,t.up having count(*)>1)
or (t.right,t.left,t.down) in (select t.right,t.left,t.down from test_table t group by t.right,t.left,t.down having count(*)>1)
or (t.right,t.up,t.down) in (select t.right,t.up,t.down from test_table t group by t.right,t.up,t.down having count(*)>1)
or (t.left,t.up,t.down) in (select t.left,t.up,t.down from test_table t group by t.left,t.up,t.down having count(*)>1)
and t.mud_id=285 order by t.right,t.left,t.up,t.down ;
if it correct it's go in loop without result for more than 10m waiting
if not
please what is the correct query to get the result
Since there are some assumptions to be made on how how to order and/or organize the results, you may use the query below to identify each matching pair.
DECLARE #T TABLE(ID INT, [right] VARCHAR(20), [left] varchar(20), up
varchar(20), down varchar(20))
INSERT INTO #T VALUES
(1,'street','hospital','coffee','building'),
(2,'house','hospital','coffee','building'),
(3,'road','bus','coffee','sign'),
(4,'house','hospital','coffee','sign'),
(5,'car','road','coffee','sign'),
(6,'street','road','coffee','sign')
SELECT
T1.ID AS MatchID1,
T2.ID AS MatchID2
FROM
#T T1
INNER JOIN #T T2 ON T1.ID <> T2.ID
GROUP BY
T1.ID, T2.ID
HAVING
MAX(CASE WHEN T1.[right] = T2.[right] THEN 1 ELSE 0 END +
CASE WHEN T1.[left] = T2.[left] THEN 1 ELSE 0 END +
CASE WHEN T1.up = T2.up THEN 1 ELSE 0 END +
CASE WHEN T1.down = T2.down THEN 1 ELSE 0 END) = 3
MatchID1 MatchID2
2 1
1 2
4 2
2 4
6 5
5 6
A trick to bring back matches by first occurrence using:
INNER JOIN #T T2 ON T1.ID > T2.ID
MatchID1 MatchID2
1 2
2 4
5 6

SQL Join instead of UNION

I have the following tables:
Table T1
--------------------------
ZID | NAME
------------------------
1 | Test
Table C1
--------------------------
ZID | VALUE1
------------------------
1 | A
1 | B
Table S1
--------------------------
ZID | VALUE2
------------------------
1 | C
1 | D
I would like a select query to fetch the values below
----------------------------------
ZID | NAME | VALUE1 | VALUE2
----------------------------------
1 | Test | A | null
1 | Test | B | null
1 | Test | null | C
1 | Test | null | D
I am able to achieve the above by using a UNION by faking a column in each select as below.
SELECT
ZID,
NAME,
VALUE1,
NULL AS VALUE2
FROM
T1
LEFT JOIN C1 ON (
T1.ZID = C1.ZID
)
UNION ALL
(
SELECT
ZID,
NAME,
NULL AS VALUE1,
VALUE2
FROM
T1
LEFT JOIN S1 ON (
T1.ZID = S1.ZID
)
);
Would it be possible to retrieve data as above in a single select using JOINS? I have tried using left join and however, I end up with a Cartesian product of data in the results.
You could use:
select t1.*, c1.value1, s1.value2
from c1 full join
s1
on false join
t1
on t1.id in (c1.id, s1.id)
A full join on false is a lot like union all.
Query will be something like:
SELECT ZID, NAME, VALUE1, VALUE2
FROM T1, C1, S1;
Union will give you more rows. Join will give you more columns. If you want to join them to get one set of rows with all columns, you need to specify which rows go together. For example, if you do not specify which C1 row goes with which S1 row, it matches each to all of them, and gives the product you do not want.
Define the relationship in the on clause, and it will do what you want.
You need the UNION to add rows. You can put the join on the outside of the UNION to avoid having to do it twice:
SELECT
ZID,
NAME,
VALUE1,
NULL AS VALUE2
FROM
T1
LEFT JOIN (
SELECT ZID, VALUE1, NULL AS VALUE2
FROM C1
UNION ALL
SELECT ZID, NULL AS VALUE1, VALUE2
FROM S1
) Merged ON T1.ZID = Merged.ZID

How to select from one table non existing data in second table?

I have 2 tables :
tab1
tab2
Here's the rows included in those table
tab1
item| num
a | 2
a | 3
b | 1
b | 3
tab2
num
1
2
3
4
5
There is a reference between the num in tab1 and num in tab2.
How to select nums from tab2 that dont exist in tab1 with the item column?
Here is the desired result:
item | num
a | 1
a | 4
a | 5
b | 2
b | 4
b | 5
One approach would be anti-join, using a "calendar" table containing all possible num/item values:
SELECT DISTINCT
t1a.item,
t2.num
FROM tab2 t2
CROSS JOIN tab1 t1a
LEFT JOIN tab1 t1b
ON t1b.item = t1a.item AND
t1b.num = t2.num
WHERE
t1b.item IS NULL
ORDER BY
t1a.item,
t2.num;
Demo

How to do an outer join with full result between two tables

I have two tables:
TABLE1
id_attr
-------
1
2
3
TABLE2
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
As a result I want a table that show:
RESULT
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
So I want the row with id=10 and id_attr=3 also when id_Attr=3 is missing in TABLE2 (and I know that because I have a NULL value (or something else) in the val column of RESULT.
NB: I could have others ids in table2. For example, after insert this row on table2: {11,1,A}, as RESULT I want:
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
11 | 1 | A
11 | 2 | NULL
11 | 3 | NULL
So, for every id, I want always the match with all id_attr.
Your specific example only has one id, so you can use the following:
select t2.id, t2.id_attr, t2.val
from table2 t2
union all
select 10, t1.id_attr, NULL
from table1 t1
where not exists (select 1 from table2 t2 where t2.id_attr = t1.id_attr);
EDIT:
You can get all combinations of attributes and ids in the following way. Use a cross join to create all the rows you want and then a left join to bring in the data you want:
select i.id, t1.id_attr, t2.val
from (select distinct id from table2) i cross join
table1 t1 left join
table2 t2
on t2.id = i.id and t2.id_attr = t1.id_attr;
It sounds like you want to do just an outer join on id_attr instead of id.
select * from table2 t2
left outer join table1 t1 on t2.id_attr = t1.id_attr;

sql problem,challenge

I want to get
id a b c
--------------------
1 1 100 90
6 2 50 100
...from:
id a b c
--------------------
1 1 100 90
2 1 300 50
3 1 200 20
4 2 200 30
5 2 300 70
6 2 50 100
It's the row with the smallest b group by a.
How to do it with sql?
EDIT
I thought it can be achieved by
select * from table group by a having min(b);
which I found later it's wrong.
But is it possible to do it with having statement?
I'm using MySQL
SELECT t1.*
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON (t1.a=t2.a AND t1.b>t2.b)
WHERE t2.a IS NULL;
This works because there should be no matching row t2 with the same a and a lesser b.
update: This solution has the same issue with ties that other folks have identified. However, we can break ties:
SELECT t1.*
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON (t1.a=t2.a AND (t1.b>t2.b OR t1.b=t2.b AND t1.id>t2.id))
WHERE t2.a IS NULL;
Assuming for instance that in the case of a tie, the row with the lower id should be the row we choose.
This doesn't do the trick:
select * from table group by a having min(b);
Because HAVING MIN(b) only tests that the least value in the group is not false (which in MySQL means not zero). The condition in a HAVING clause is for excluding groups from the result, not for choosing the row within the group to return.
In MySQL:
select t1.* from test as t1
inner join
(select t2.a, min(t2.b) as min_b from test as t2 group by t2.a) as subq
on subq.a=t1.a and subq.min_b=t1.b;
Here is the proof:
mysql> create table test (id int unsigned primary key auto_increment, a int unsigned not null, b int unsigned not null, c int unsigned not null) engine=innodb;
Query OK, 0 rows affected (0.55 sec)
mysql> insert into test (a,b,c) values (1,100,90), (1,300,50), (1,200,20), (2,200,30), (2,300,70), (2,50,100);
Query OK, 6 rows affected (0.39 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql> select * from test;
+----+---+-----+-----+
| id | a | b | c |
+----+---+-----+-----+
| 1 | 1 | 100 | 90 |
| 2 | 1 | 300 | 50 |
| 3 | 1 | 200 | 20 |
| 4 | 2 | 200 | 30 |
| 5 | 2 | 300 | 70 |
| 6 | 2 | 50 | 100 |
+----+---+-----+-----+
6 rows in set (0.00 sec)
mysql> select t1.* from test as t1 inner join (select t2.a, min(t2.b) as min_b from test as t2 group by t2.a) as subq on subq.a=t1.a and subq.min_b=t1.b;
+----+---+-----+-----+
| id | a | b | c |
+----+---+-----+-----+
| 1 | 1 | 100 | 90 |
| 6 | 2 | 50 | 100 |
+----+---+-----+-----+
2 rows in set (0.00 sec)
Use:
SELECT DISTINCT
x.*
FROM TABLE x
JOIN (SELECT t.a,
MIN(t.b) 'min_b'
FROM TABLE T
GROUP BY t.a) y ON y.a = x.a
AND y.min_b = x.b
You're right. select min(b), a from table group by a. If you want the entire row, then you've use analytics function. That depends on database s/w.
It depends on the implementation, but this is usually faster than the self-join method:
SELECT id, a, b, c
FROM
(
SELECT id, a, b, c
, ROW_NUMBER() OVER(PARTITION BY a ORDER BY b ASC) AS [b IN a]
) As SubqueryA
WHERE [b IN a] = 1
Of course it does require that you SQL implementation be fairly up-to-date with the standard.