Union three tables and show where data came from - sql

have no idea how to solve following:
There are three tables each with a column of names, for example
Table 1 - column name 'Name' - values 'A', 'B' and 'C'
Table 2 - column name 'Name' - values 'A' and 'B'
Table 3 - column nane 'Name' - values 'A' and 'C'
The goal is to UNION the tables - each value of the three tables should be shown only one time. In addition there should be three new "virtual columns" showing in which table the value is included('1' when value is included, '0' if not). So the result should look like this:
Value | Table1 | Table2 | Table3
--------------------------------
A | 1 | 1 | 1
B | 1 | 1 | 0
C | 1 | 0 | 1
Hope someone can help me, thanks in advance.

Does this do what you want?
select Name, max(Table1) as Table1, max(Table2) as Table2, max(Table3) as Table3
from (select Name, 1 as Table1, 0 as Table2, 0 as Table3
from table1
union all
select Name, 0 as Table1, 1 as Table2, 0 as Table3
from table2
union all
select Name, 0 as Table1, 0 as Table2, 1 as Table3
from table3
) t
group by Name;
You might want to use sum() instead of max() to get the number of times the value occurs in each table.

If your db supports full joins you can try the query below.
select
coalesce(t1.Name,t2.Name,t3.Name) myValue,
(case when max(t1.Name) is not null then 1 else 0 end) c1,
(case when max(t2.Name) is not null then 1 else 0 end) c2,
(case when max(t3.Name) is not null then 1 else 0 end) c3
from
Table1 t1
full join Table2 t2 on t1.Name = t2.Name
full join Table3 t3 on t1.Name = t3.Name
group by coalesce(t1.Name,t2.Name,t3.Name)
If you know that a value will not appear more than once in each table, you can remove the group by and max parts.

Here's my attempt (update: works f.e. in SQL-Server 2005 and upwards due to the CTE):
With names AS
(
SELECT Name, Source = 'T1'
FROM dbo.Table1
UNION ALL
SELECT Name, Source = 'T2'
FROM dbo.Table2
UNION ALL
SELECT Name, Source = 'T3'
FROM dbo.Table3
)
SELECT n.Name,
Table1 = CASE WHEN EXISTS
(SELECT 1 FROM names n2
WHERE Source = 'T1' AND n2.Name=n.Name)
THEN 1 ELSE 0 END,
Table2 = CASE WHEN EXISTS
(SELECT 1 FROM names n2
WHERE Source = 'T2' AND n2.Name=n.Name)
THEN 1 ELSE 0 END,
Table3 = CASE WHEN EXISTS
(SELECT 1 FROM names n2
WHERE Source = 'T3' AND n2.Name=n.Name)
THEN 1 ELSE 0 END
FROM names n
GROUP BY n.Name
Demo

Related

Comparing a particular column value for two rows in SQL

I have a table as follows:
ID Version Type Value
121 1 A 100
121 1 B 200
122 2 A 300
122 2 B 300
123 3 A 300
123 3 B 300
124 4 A 420
124 4 B 420
121 3 A 410
121 3 B 410
I am trying to build a query which gives returns the rows where "Value" is different between Type A and Type B for same ID and Version.
For example, in the above scenario, ID and Version is Unique 5 times (121-1,122-2,123-3,124-4,121-3). I would look at "Type" for each unique group and compare "Value" for Type A and Type B.
The above scenario should return:
ID Version Type Value
121 1 A 100
121 1 B 200
The above two rows are the only ones with different "Value" for unique ID and Version and among Type A and Type B.
Any help will be greatly appreciated :)
You can use exixts :
select t.*
from table t
where exists (select 1
from table t1
where t1.id = t.id and
t1.Version = t.Version and
t1.type <> t.type and
t1.Value <> t.Value
);
You can join a table to itself:
SELECT *
FROM [MyTable] t0
INNER JOIN [MyTable] t1 ON t1.ID = t0.ID AND t0.Type <> t1.Type AND t0.Value <> t1.Value
or
SELECT *
FROM [MyTable] t0
INNER JOIN [MyTable] t1 ON t1.ID = t0.ID AND t1.Type = 'B' AND t0.Value <> t1.Value
WHERE t0.Type = 'A'
If you have only two types, I am going to recommend a different result set format:
select id, version,
max(case when type = 'A' then value end) as value_a,
max(case when type = 'B' then value end) as value_b
from t
where type in ('A', 'B')
group by id, version,
having max(case when type = 'A' then value end) <> max(case when type = 'B' then value end);
I recommend this because a typical follow-on question might be "how big is the difference".
This is one way to approach this problem:
select *
from t1
where (id, value, version) in (select id, value, version
from t1
group by id, value, version
having count(id) = 1);
Here is a DEMO
This is what I ended up doing:
SELECT MAX(Value), MIN(Value)
FROM table_name
GROUP BY ID, Version
HAVING MAX(Value)<>MIN(Value);

Find the 2nd row of data based on a column value, if not available then print NULL

I am working on some project in hive and below problem is one part of it. Please note that i am going to apply the solution for this query on a subquery result /table which i have already made.
So as part of this, i am trying to print the row for each id based on column value of rnum. If rnum=2 then print both the column values, i.e., id,value. If rnum= 2 is not present for an id then then print id,'No value'.
Give below sample input and expected output.
For eg.
for id 100, since rnum is only 1, hence print value as 'No value'
for id 200, print only value for rnum=2, i.e., xyz and ignore value for rnum=1,3 and 4
Input:
id value rnum
100 abc 1
200 def 1
200 xyz 2
200 rtz 3
200 tgv 4
Expected output:
id value
100 No Value
200 xyz
You can use aggregation and case logic:
select id,
(case when sum(case when mum = 2 then 1 else 0 end) > 0
then max(case when mum = 2 then value end)
else 'No Value'
end)
from t
group by id;
You can do it as shown below:
select a.id, case when t.id is null then 'No value' else t.value end
from (select distinct id as id from t) a
left join t on t.rnum = 2 and a.id = t.id
Here's your query.
select t1.id, case when t2.rnum != 1 then t2.value else 'no value' end
from (
select count(1) as ct, id from test2
group by id) t1
left join test2 t2 on t2.id = t1.id and t2.rnum = 2
order by t1.id asc
Changed, allowing for any id values:
with t2(id, cnt) as
(
select id, count(*) as cnt
from t1
group by id
)
select t1.id,
max(case
when t2.cnt = 1 and t1.rnum = 1 then 'No Value'
when t2.cnt > 1 and t1.rnum = 2 then value
end)
from t1
join t2 on t2.id = t1.id
group by t1.id

Return id when id value does not equal 0 at least once

I have 2 tables.
table_1
id | product
1 | a
2 | b
3 | c
4 | d
table_2
product_id | value
1 | 0
2 | 0
1 | 5
2 | 0
4 | 10
How can I return details from table_1 for ids that:
- are present in table_2 (table_1.id = table_2.product_id)
- do not have any associated value equal to 0 (for example id "1" should be excluded)
The correct result would be id "4" as none of its values equal to zero.
I have tried below query but it returns also id "3" that is not present in the table_2.
SELECT * FROM table_1
WHERE id NOT IN (
SELECT product_id FROM table_2
WHERE value = 0)
You can use two conditions:
SELECT t1.*
FROM table_1 t1
WHERE EXISTS (SELECT 1
FROM table_2 t2
WHERE t1.id = t2.product_id
) AND
NOT EXISTS (SELECT 1
FROM table_2 t2
WHERE t1.id = t2.product_id AND t2.value = 0
);
The naive approach:
-- Step 1: Select product IDs to ignore
SELECT product_id
FROM table_2
WHERE value = 0
-- Step 2: Select product IDs to include
SELECT product_id
FROM table_2
WHERE product_id NOT IN ( -- Use the result of Step 1
SELECT product_id
FROM table_2
WHERE value = 0
)
-- Final query: Select products
SELECT *
FROM table_1
WHERE product_id IN ( -- Use the result of Step 2
SELECT product_id
FROM table_2
WHERE product_id NOT IN ( -- Use the result of Step 1
SELECT product_id
FROM table_2
WHERE value = 0
)
)
One option, using aggregation:
SELECT
t1.id,
t1.product
FROM table_1 t1
INNER JOIN table_2 t2
ON t1.id = t2.product_id
GROUP BY
t1.id,
t1.product
HAVING
COUNT(CASE WHEN t2.value = 0 THEN 1 END) = 0;
In order for the HAVING clause to return true, the product must not have had any zero value in the second table. Also, the inner join filters off any product which does not appear at all in the second table.
You can get the ids you need to use for the IN clause, by grouping by product_id and putting the condition in the HAVING clause:
SELECT * FROM table_1
WHERE id IN (
SELECT product_id
FROM table_2
GROUP BY product_id
HAVING SUM(CASE WHEN value = 0 THEN 1 ELSE 0 END) = 0
)

TSQL - Select rows with same column A but different column B

I'm trying to find rows (Name) that does not have ID = 1. For example, if my table looked like this:
Name ID
--------------
A 1
A 0
B 1
B 0
C 0
D 2
D 0
The answer to this query would be:
Name
-----
C
D
Do you have any idea?
SELECT Name
FROM myTable
GROUP BY Name
HAVING SUM(CASE WHEN ID = 1 THEN 1 ELSE 0 END) = 0
Here is one way to do it:
SELECT DISTINCT Name
FROM Table t0
WHERE NOT EXISTS
(
SELECT 1
FROM Table t1
WHERE t0.Name = t1.Name
AND t1.Id = 1
)
Try this query:
SELECT DISTINCT(name)
FROM tbl t1
WHERE
NOT EXISTS (SELECT name FROM tbl t2 WHERE ID=1 AND t1.name=t2.name)
Select Distinct name
From myTable
Where name not in (Select name From myTable Where id= 1)

SQL: Outputting Multiple Rows When Joining From Same Table

My question is this: Is it possible to output multiple rows when joining from the same table?
With this code for example, I would like it to output 2 rows, one for each table. Instead, what it does is gives me 1 row with all of the data.
SELECT t1.*, t2.*
FROM table t1
JOIN table t2
ON t2.id = t1.oldId
WHERE t1.id = '1'
UPDATE
Well the problem that I have with the UNION/UNION ALL is this: I don't know what the t1.oldId value is equal to. All I know is the id for t1. I am trying to avoid using 2 queries so is there a way I could do something like this:
SELECT t1.*
FROM table t1
WHERE t1.id = '1'
UNION
SELECT t2.*
FROM table t2
WHERE t2.id = t1.oldId
SAMPLE DATA
messages_users
id message_id user_id box thread_id latest_id
--------------------------------------------------------
8 1 1 1 NULL NULL
9 2 1 2 NULL 16
10 2 65 1 NULL 15
11 3 65 2 2 NULL
12 3 1 1 2 NULL
13 4 1 2 2 NULL
14 4 65 1 2 NULL
15 5 65 2 2 NULL
16 6 1 1 2 NULL
Query:
SELECT mu.id FROM messages_users mu
JOIN messages_users mu2 ON mu2.latest_id IS NOT NULL
WHERE mu.user_id = '1' AND mu2.user_id = '1' AND ((mu.box = '1'
AND mu.thread_id IS NULL AND mu.latest_id IS NULL) OR mu.id = mu2.latest_id)
This query fixes my problem. But it seems the answer to my question is to not use a JOIN but a UNION.
You mean one row for t1 and one row from t2?
You're looking for UNION, not JOIN.
select * from table where id = 1
union
select * from table where oldid = 1
If you are trying to multiply rows in a table, you need UNION ALL (not UNION):
select *
from ((select * from t) union all
(select * from t)
) t
I also sometimes use a cross join to do this:
select *
from t cross join
(select 1 as seqnum union all select 2) vals
The cross join is explicitly multiplying the number of rows, in this case, with a sequencenumber attached.
Well, since it's the same table, you could do:
SELECT t2.*
FROM table t1
JOIN table t2
ON t2.id = t1.oldId
OR t2.id = t1.id
WHERE t1.id = '1'