SQL query on same duplicate tables - sql

Working with Access, I have the following table
ID Root_ID Level Code
S1 S 10 ABC
S3 S 20 DFG
L4 L 10 FFF
L4 L 20 GGG
F2 F 10 ABC
What I'm looking for is: rows having the same code, on the same level but different Root_IDs.
I created a query with twice the same table T and an innner join on both the Level and the code. I tried this first before trying to identify the different Root_IDs but returned results were wrong...
Here for example, the result should be:
ID Root_ID Level Code
S1 S 10 ABC
F2 F 10 ABC
Thanks for your help!

You can find the code, level for which there are more than one unique root_ids in a subquery and then join it with table to get the complete rows.
select a.*
from your_table as a
inner join (
select code, level
from your_table
group by code, level
having count(distinct root_id) > 1
) as b on a.code = b.code
and a.level = b.level

Try this
select t1.*
from your_table as t1
inner join (
select code, level
from your_table
group by code, level
having min(root_id) <> max(root_id)
) as t2 on t1.code = t2.code
and t1.level = t2.level

Related

Insert missing values in column at all levels of another column in SQL?

I have been working with some big data in SQL/BigQuery and found that it has some holes in it that need to be filled with values in order to complete the dataset. What I'm struggling with is how to insert the missing values properly.
Say that I have multiple levels of a variable (1, 2, 3... no upper bound) and for each of these levels, they should have an A, B, C value. Some of these records will have data, others will not.
Current dataset:
level value data
1 A 1a_data
1 B 1b_data
1 C 1c_data
2 A 2a_data
2 C 2c_data
3 B 3b_data
What I want the dataset to look like:
level value data
1 A 1a_data
1 B 1b_data
1 C 1c_data
2 A 2a_data
2 B NULL
2 C 2c_data
3 A NULL
3 B 3b_data
3 C NULL
What would be the best way to do this?
You need a CROSS join of the distinct levels with the distinct values and a LEFT join to the table:
SELECT l.level, v.value, t.data
FROM (SELECT DISTINCT level FROM tablename) l
CROSS JOIN (SELECT DISTINCT value FROM tablename) v
LEFT JOIN tablename t ON t.level = l.level AND t.value = v.value
ORDER BY l.level, v.value;
See the demo.
We can use an INSERT INTO ... SELECT with the help of a calendar table:
INSERT INTO yourTable (level, value, data)
SELECT t1.level, t2.value, NULL
FROM (SELECT DISTINCT level FROM yourTable) t1
CROSS JOIN (SELECT DISTINCT value FROM yourTable) t2
LEFT JOIN yourTable t3
ON t3.level = t1.level AND
t3.value = t2.value
WHERE t3.data IS NULL;

Listagg function is not working in sub query

Listagg function is not working when it used in subquery, though using listagg function in prod column is not concatenated all the products in single row
select
a.id,
a.num,
(listagg(c.prod_name,',') within group(order by prod_name)
from product c
where c.prod_id = NVL(b.prod_id,b.prod_pos) As prod
from master a, base_product b
where
b.id = a.id and
b.type = 1 and
a.id = 12345;
Your subquery in the select clause is lacking a select keyword, this is probably the immediate source of the error. However, we can improve upon your query and instead join to a subquery which does the aggregation:
select
a.id,
a.num,
coalesce(c.prod, 'NA') as prod
from master a
inner join base_product b
on b.id = a.id
left join
(
select listagg(prod_name, ',') within group(order by prod_name) as prod
from product
) c
on c.prod_id = NVL(b.prod_id, b.prod_pos)
where
b.type = 1 and
a.id = 12345;
Besides refactorting the call to listagg, I also replaced your implicit join syntax with an explicit inner join. This is the preferred way of writing the query, and this style because part of the ANSI SQL standard 25 years ago.
You can use listagg(prod, ',') within group(ORDER BY prod) with group by on column num in cte or derived table and join it to the main table to get id as below.
SELECT t2.id,
t2.num,
t1.prod
FROM
(SELECT num,
listagg(prod, ',') within group(
ORDER BY prod) AS prod
FROM table1
GROUP BY num ) t1
JOIN table1 t2 ON t1.num = t2.num
order by t2.id\\
OR
WITH t1 AS
(SELECT num,
listagg(prod, ',') within group(
ORDER BY prod) AS prod
FROM table1
GROUP BY num)
SELECT t2.id,
t2.num,
t1.prod
FROM t1
JOIN table1 t2 ON t1.num = t2.num
ORDER BY t2.id\\
Given you sample data:
ID NUM PROD
--------------------
101 1701A001 book
102 1701A001 data
103 1702B005 bat
104 1702B005 ball
105 1703C006 Stumps
Result:
ID NUM PROD
-------------------------
101 1701A001 book,data
102 1701A001 book,data
103 1702B005 ball,bat
104 1702B005 ball,bat
105 1703C006 Stumps
DEMO

Chaining joins in SQL based on dynamic table

The title may not be accurate for the question but here goes! I have the following table:
id1 id2 status
1 2 a
2 3 b
3 4 c
6 7 d
7 8 e
8 9 f
9 10 g
I would like to get the first id1 and last status based on a dynamic chain joining, meaning that the result table will be:
id final_status
1 c
6 g
Logically, I want to construct the following arrays based on joining the table to itself:
id1 chained_ids chained_status
1 [2,3,4] [a,b,c]
6 [7,8,9,10] [d,e,f,g]
Then grab the last element of the chained_status list.
Since if we were to keep joining this table to itself on id1 = id2 we would eventually have single rows with these results. The problem is that the number of joins is not constant (a single id may be chained many or few times). There is always a 1 to 1 mapping of id1 to id2.
Thanks in advanced! This can be done in either T-SQL or Hive (if someone has a clever map-reduce solution).
You can do this with a recursive CTE:
;WITH My_CTE AS
(
SELECT
id1,
id2,
status,
1 AS lvl
FROM
My_Table T1
WHERE
NOT EXISTS
(
SELECT *
FROM My_Table T2
WHERE T2.id2 = T1.id1
)
UNION ALL
SELECT
CTE.id1,
T3.id2,
T3.status,
CTE.lvl + 1
FROM
My_CTE CTE
INNER JOIN My_Table T3 ON T3.id1 = CTE.id2
)
SELECT
CTE.id1,
CTE.status
FROM
My_CTE CTE
INNER JOIN (SELECT id1, MAX(lvl) AS max_lvl FROM My_CTE GROUP BY id1) M ON
M.id1 = CTE.id1 AND
M.max_lvl = CTE.lvl

using combination of select, union and group by

I have two similar tables and a third table with one shared column (sms_status_id) with first and second table. I want to select similar columns from first and second table, union them with third table and then remove repetitive rows.
This is what I've done so far:
select *
from
(
select sms_log_id, atm_id, device_id, error_id, datetime, retries, message
from first_table
join third_table
on sms_log_id = sms_status_id
)
union
(
select sms_id, atm_id, device_id, error_id, datetime, retries, message
from second_table
join third_table
on sms_id = sms_status_id
)
This gives me what I want but the result has repetitive rows. I tried to use
GROUP BY sms_status_id
but I didn't know where should I put it so didn't work out. Any suggestions??
This is w
There is some error in your query.
Please see below code, if this will answer to your question.
#dnoeth is right, UNION always returns distinct rows otherwise use UNION ALL.
mytable1
sms_log_id | Detail
1 a
1 b
1 c
mytable2
sms_id | Detail
1 a
2 b
1 d
1 e
mytable3
id | Status
1 Approve
2 Disapprove
Query:
SELECT t1.sms_log_id as t3_id, t1.Detail, t3.Status
FROM mytable1 as t1
LEFT JOIN mytable3 as t3
ON t1.sms_log_id = t3.id
UNION
SELECT t2.sms_id as t3_id, t2.Detail, t3.Status
FROM mytable2 as t2
LEFT JOIN mytable3 as t3
ON t2.sms_id = t3.id
Result:
t3_id Detail Status
1 a Approve
1 b Approve
1 c Approve
2 b Disapprove
1 d Approve
1 e Approve
you can't see in the result a from mytable2 because it is already in mytable1, and b is printed because it has different status.
Demo

SQL select 1 to many within the same row

I have a table with 1 record, which then ties back to a secondary table which can contain either no match, 1 match, or 2 matches.
I need to fetch the corresponding records and display them within the same row which would be easy using left join if I just had 1 or no matches to tie back, however, because I can get 2 matches, it produces 2 records.
Example with 1 match:
Select T1.ID, T1.Person1, T2.Owner
From T1
Left Join T2
ON T1.ID = T2.MatchID
Output
ID Person1 Owner1
----------------------
1 John Frank
Example with 2 match:
Select T1.ID, T1.Person1, T2.Owner
From T1
Left Join T2
ON T1.ID = T2.MatchID
Output
ID Person1 Owner
----------------------
1 John Frank
1 John Peter
Is there a way I can formulate my select so that my output would reflect the following When I have 2 matches:
ID Person1 Owner1 Owner2
-------------------------------
1 John Frank Peter
I explored Oracle Pivots a bit, however couldn't find a way to make this work. Also explored the possibility of using left join on the same table twice using MIN() and MAX() when fetching the matches, however I can only see myself resorting this as a "no other option" scenario.
Any suggestions?
** EDIT **
#ughai - Using CTE does address the issue to some extent, however when attempting to retrieve all of the records, the details derived from this common table isn't showing any records on the LEFT JOIN unless I specify the "MatchID" (CASE_MBR_KEY) value, meaning by removing the "where" clause, my outer joins produce no records, even though the CASE_MBR_KEY values are there in the CTE data.
WITH CTE AS
(
SELECT TEMP.BEAS_KEY,
TEMP.CASE_MBR_KEY,
TEMP.FULLNAME,
TEMP.BIRTHDT,
TEMP.LINE1,
TEMP.LINE2,
TEMP.LINE3,
TEMP.CITY,
TEMP.STATE,
TEMP.POSTCD,
ROW_NUMBER()
OVER(ORDER BY TEMP.BEAS_KEY) R
FROM TMP_BEN_ASSIGNEES TEMP
--WHERE TEMP.CASE_MBR_KEY = 4117398
)
The reason for this is because the ROW_NUMBER value, given the amount of records won't necessarily be 1 or 2, so I attempted the following, but getting ORA-01799: a column may not be outer-joined to a subquery
--// BEN ASSIGNEE 1
LEFT JOIN CTE BASS1
ON BASS1.CASE_MBR_KEY = C.CASE_MBR_KEY
AND BASS1.R IN (SELECT min(R) FROM CTE A WHERE A.CASE_MBR_KEY = C.CASE_MBR_KEY)
--// END BA1
--// BEN ASSIGNEE 2
LEFT JOIN CTE BASS2
ON BASS2.CASE_MBR_KEY = C.CASE_MBR_KEY
AND BASS2.R IN (SELECT MAX(R) FROM CTE B WHERE B.CASE_MBR_KEY = C.CASE_MBR_KEY)
--// END BA2
** EDIT 2 **
Fixed the above issue by moving the Row number clause to the "Where" portion of the query instead of within the JOIN clause. Seems to work now.
You can use CTE with ROW_NUMBER() with 2 LEFT JOIN OR with PIVOT like this.
SQL Fiddle
Query with Multiple Left Joins
WITH CTE as
(
SELECT MatchID,Owner,ROW_NUMBER()OVER(ORDER BY Owner) r FROM t2
)
select T1.ID, T1.Person, t2.Owner as Owner1, t3.Owner as Owner2
FROM T1
LEFT JOIN CTE T2
ON T1.ID = T2.MatchID AND T2.r = 1
LEFT JOIN CTE T3
ON T1.id = T3.MatchID AND T3.r = 2;
Query with PIVOT
WITH CTE as
(
SELECT MatchID,Owner,ROW_NUMBER()OVER(ORDER BY Owner) R FROM t2
)
SELECT ID, Person,O1,O2
FROM T1
LEFT JOIN CTE T2
ON T1.ID = T2.MatchID
PIVOT(MAX(Owner) FOR R IN (1 as O1,2 as O2));
Output
ID PERSON OWNER1 OWNER2
1 John Maxwell Peter
If you know there are at most two matches, you can also use aggregation:
Select T1.ID, T1.Person1,
MIN(T2.Owner) as Owner1,
(CASE WHEN MIN(t2.Owner) <> MAX(t2.Owner) THEN MAX(t2.Owner) END) as Owner2
From T1 Left Join
T2
on T1.ID = T2.MatchID
Group By t1.ID, t1.Person1;