Remove select subquery from column list to main query - sql

Query 1 (Before):
select ta.C1,
(SELECT tb.C1 from T2 tb WHERE tb.C2 = ta.C2)
from T1 ta
WHERE ta.C3=30025239;
I want to remove the subquery from column level.
I modified the code to add join
Query 2 (After):
select ta.C1, tb.C1
from T1 ta left outer join
T2 tb
on tb.C2 = ta.C2
WHERE ta.C3=30025239;
But if subquery returns blank (no value) then Query 1 returns data for ta.C1 and null for tb.C1 whereas Query 2 will return blank (no result).
I want result of Query 2 as same as Query 1

Why do you think your two queries are giving different responses? They are equivalent:
WITH t1 AS (SELECT 1 c1, 10 c2, 30025239 c3 FROM dual UNION ALL
SELECT 2 c1, 20 c2, 30025239 c3 FROM dual UNION ALL
SELECT 3 c1, 30 c2, 30025238 c3 FROM dual),
t2 AS (SELECT 100 c1, 10 c2 FROM dual UNION ALL
SELECT 300 c1, 30 c2 FROM dual)
select 'query 1' qry, ta.C1 ta_c1, (SELECT tb.C1 from T2 tb WHERE tb.C2 = ta.C2) tb_c1
from T1 ta WHERE ta.C3=30025239
UNION ALL
select 'query 2' qry, ta.C1, tb.C1
from T1 ta left outer join T2 tb on tb.C2 = ta.C2
WHERE ta.C3=30025239;
QRY TA_C1 TB_C1
------- ---------- ----------
query 1 1 100
query 1 2
query 2 1 100
query 2 2
The only difference with query 2 is that you're no longer benefiting from the scalar subquery caching you're getting in query 1. That may not matter if the ta.c2 column is unique or doesn't contain many repeated values.

Both the queries are same. However in question queries and their output columns are different. There may be more conditions in your query that changes the output.

Related

SQL Query -Without using nested subqueries

Table1
ID SystemID Description
---------------------------
1 25 Test1
1 25 Test2
2 40 Test1
2 40 Test3
3 26 Test9
3 36 Test5
4 70 Test2
4 70 Test9
Table2
ID Department
------------------
1 Sales
2 Marketing
3 Accounting
4 Purchasing
I have these 2 tables, Table1 and Table2.
I need to select all the distinct ids from Table1 that have the same description as ID = 1 and SystemID = 25, and then select all the rows from Table2 from the query result.
Is there a better way to query for this, without using nested subqueries?
select *
from Table2
where ID in (select distinct(ID)
from Table1
where SystemID = 25
and Description in (select Description
from Table1
where ID = 1 and SystemID = 25))
Final output is
1 Sales
2 Marketing
4 Purchasing
Any help is appreciated. Thank you.
I think you want:
select t1.id, t2.department
from table1 t1 join
table2 t2
on t1.id = t2.id
where t1.description in (select tt1.description from table1 tt1 where tt1.id = 1 and tt1.systemid = 25);
This is standard SQL and should work in both SQL Server and Oracle.
You can also use a modification of an outer join to detect presence of a value.
SELECT DISTINCT t2.ID, t2.DEPARTMENT
FROM
table2 AS t2
INNER JOIN table1 AS t1a ON table2.ID = table1.ID
LEFT OUTER JOIN table1 AS t1b ON t1b.id = 1 AND t1b.systemID = 25 AND t1b.description = t1a.description
WHERE t1b.ID IS NOT NULL
AND t1a.systemID = 25
This will filter out all entries who don't have a description matching an entry with id 1 and systemID 25
I believe this should give you the same result. Instead of using an IN I used an EXISTS and then instead of a futher subquery you can then use a JOIN:
SELECT *
FROM Table2 T2
WHERE EXISTS (SELECT 1
FROM Table1 T1
JOIN Table1 T1t ON T1.[Description] = T1t.[Description]
WHERE T1.ID = T2.ID
AND T1t.ID = 1 AND T1t.SystemID = 25);
SELECT DISTINCT T2.* --Use a distinct for simplicity but a group by is better
FROM Table2 AS T2
INNER JOIN Table1 AS T1_Source ON T1_Source.SystemID = 25 AND T1_Source.ID = 1
/*^ Find table1 with System and ID
Expected Result
ID SystemID Description
1 25 Test1
1 25 Test2
Note Rows are duplicated use distinct or group by
*/
INNER JOIN Table1 AS T1_Target ON T1_Target.Description = T1_Source.Description
/*^ Find table1 with all the Description matching the result we found
Expected Result
ID SystemID Description
1 25 Test1
1 25 Test2
2 40 Test1
4 70 Test2
Note Rows are duplicated use distinct or group by
*/

Query to get oldest date into a column?

I have some tables that look like this:
Table1
Column1
A
B
C
Table2
column1 Id1 Id2
A 2 100
A 3 101
B 2 100
B 3 101
C 2 100
Table3
Id2 Date Item Status
100 10/20/17 A1 A
101 10/21/17 A1 A
100 11/22/17 A2 I
101 11/23/17 A2 A
My query looks like this:
Select
Date, *
FROM TABLE1 T1
LEFT JOIN Table2 T2 ON
T1.Column1 = T2.Column2
LEFT JOIN Table3 T3 ON
T2.Id2 = T3.Id2
I would like to return the oldest date on table 3 where the status is A into the date column in my query.
I was able to return the oldest date with this query but can't integrate this into my query with joins.
SELECT
MIN(DATE)
FROM Table3
WHERE Id2 IN (100,101)
AND STATUS = 'A'
group by Id2, ITEM
How can I get the oldest date into the query that uses the join clauses?
Couple of ways to approach what I believe that you're trying to do.
I'd say the easiest would be to simplify your search parameters with a subquery of Table 3.
select * from table2 t2
LEFT JOIN
(Select Id2, Item, max(Date)from Table 3
WHERE Status = 'A'
GROUP BY Id2, Item)a ON t2.Id2 = a.Id2;
The subquery does the filtering and then a quick join to table 2 would get all your data.

SQL Complex join not giving distinct result

I have two tables :-
Table1:-
ID1
1
1
1
1
4
5
Table2:-
Id2
2
2
1
1
1
8
I want to show all the ID2 from table2 which are present in ID1 of table1 by using joins
I used :-
select ID2 from Table2 t2 left join Table1 t1
on t2.Id2=t1.Id1
But this was giving repeated result as :-
Id2
1
1
1
1
1
1
1
It should show me 1 as 3 times only as it is present in Table2 3 times.
Please help.
You're matching the value 1 with 4 rows on Table1 and 3 rows on Table2 that's why you're seeing 12 rows. You need an additional JOIN condition. You can add a ROW_NUMBER and do an INNER JOIN to achieve your desired result.
WITH Cte1 AS(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY Id1 ORDER BY (SELECT NULL))
FROM Table1
),
Cte2 AS(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY Id2 ORDER BY (SELECT NULL))
FROM Table2
)
SELECT c2.Id2
FROM Cte2 c2
INNER JOIN Cte1 c1
ON c1.Id1 = c2.Id2
AND c1.rn = c2.rn
However, you can achieve the desired result without using a JOIN.
SELECT *
FROM Table2 t2
WHERE EXISTS(
SELECT 1 FROM Table1 t1 WHERE t1.Id1 = t2.Id2
)
It's the expected behavior of Join Operation. It will match every row from the two tables, so you will get 12 rows containing value 1 in result of join query.
You can use below query to get desired result.
select ID2 from Table2 t2 WHERE ID2 IN (SELECT ID1 FROM Table1 t1)
select id2 from table2 t2 where exists ( select 1 from table1 t1 where t1.id1 = t2.id2)
Your join logic works fine, the problem is each of your ID2 is matching against all ID1s. A simple solution would be to join with a table of distinct ID1s to avoid this duplication.
select
t2.ID2
from Table2 t2
left join (select distinct * from Table1) t1
on t1.Id1=t2.Id2
where t1.ID1 is not null
;
Here is a functional example
This will select your entire ID2 list with ID1 populated in a column. ID1 is null where there was no match. Select your ID2 column from this table but just don't pull null values (with where clause):

Oracle sql - display if record exists else display parent

I want to categorize data into two types based on:
If a value exists in T2, display "A", else display "B" as "Type". Is there a way to implement this in case when or decode?
T1 is the parent of T2.
T1
1
2
3
4
5
T2
1
1
3
3
3
4
Ideally my output would be
Type
A
B
A
A
B
edit: I want to add that A and B are text values I want to display based on my above condition, this is not coming from the db. Also, T2 will not have a corresponding record at all for 2 & 5. so I cannot really check for null.
Try this:
SELECT T1.Col,
CASE WHEN T2.Col IS NOT NULL THEN 'A' ELSE 'B' END AS Type
FROM T1
LEFT JOIN (
SELECT DISTINCT Col FROM T2
) AS T2 ON T1.Col = T2.Col
Oracle Setup:
CREATE TABLE T1 ( column_name ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL < 6;
CREATE TABLE t2 ( column_name ) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 1 FROM DUAL UNION ALL
SELECT 3 FROM DUAL UNION ALL
SELECT 3 FROM DUAL UNION ALL
SELECT 4 FROM DUAL;
Query:
SELECT NVL2( T2.column_name, 'A', 'B' ) AS Type
FROM T1
LEFT OUTER JOIN
( SELECT DISTINCT column_name FROM T2 ) T2
ON T1.column_name = T2.column_name
ORDER BY T1.column_name;
Output:
TYPE
----
A
B
A
A
B

SQL - find rows that have the same set of values in a column

I have a table and I want to select all rows that have exactly the same set of values that appear in a column and return them as a pair of a certain column.
For example, let's say I have a table named Table:
C1 C2
1 1
1 2
1 3
2 1
3 1
3 2
3 3
4 1
4 2
When I run my query, it should return the row:
1 3
because these are the two values in C1 that have the same set of values in column C2 (1,2,3).
I have an incorrect query below that returns all rows that have at least one matching value in C2 and I can't figure out how to correct it.
SELECT DISTINCT T1.C1, T2.C1
FROM Table T1, Table T2
WHERE T1.C1 != T2.C1
AND T1.C2 = T2.C2
AND T1.C1 < T2.C1
GROUP BY s1.suppId, s2.suppId;
Any help would be greatly appreciated, thanks.
As you do not specify, this one will handle the case of two sets of matching C2 values, outputting two rows - one for each matched set.
with thetable as (
SELECT 1 C1, 1 c2 from dual union
SELECT 1 C1, 2 c2 from dual union
SELECT 1 C1, 3 c2 from dual union
SELECT 2 C1, 1 c2 from dual union
SELECT 3 C1, 1 c2 from dual union
SELECT 3 C1, 2 c2 from dual union
SELECT 3 C1, 3 c2 from dual union
SELECT 4 C1, 1 c2 from dual union
SELECT 4 C1, 2 c2 from dual union
-- added fourrows to give a second set of matches
SELECT 5 C1, 1 c2 from dual union
SELECT 5 C1, 2 c2 from dual union
SELECT 6 C1, 1 c2 from dual union
SELECT 6 C1, 2 c2 from dual )
SELECT LIST_C2, List_c1
FROM (
SELECT list_c2
,LISTAGG(c1,',') WITHIN GROUP (ORDER BY c1) list_c1
FROM (
SELECT c1, LISTAGG(c2,',') WITHIN GROUP (ORDER BY c2) list_c2
FROM thetable
GROUP BY c1
)
group by list_c2
)
-- only bring back where we had more than one c1
WHERE instr(list_c1,',') != 0
Showing the following two sets of C2 values and their matching lists of C1s
LIST_C2 LIST_C1
"1,2" "4,5,6"
"1,2,3" "1,3"
listagg is the rescue (instead of count as commented by #Juan)
with lst as (
select c1, LISTAGG(c2, '; ') WITHIN GROUP (ORDER BY c2) c2_lst
from tst
group by c1
)
select lst1.c1 c1a, lst2.c1 c1b
from lst lst1
inner join lst lst2
on lst1.c2_lst = lst2.c2_lst and
lst1.c1 < lst2.c1
;
gives as requested
1 3
SQLFiddleDemo
Also be prepared, this works only small data (the concatenated key is limited with 4000 bytes).
UPDATE
listagg concatenates all values in the group. To get only distinct values (i.e. set of values) an additional query with DISTINCT must be performed.
WITH lst AS
( SELECT DISTINCT c1,c2 FROM tst
),
lst_dist AS
(SELECT c1,
LISTAGG(c2, '; ') WITHIN GROUP (
ORDER BY c2) c2_lst
FROM lst
GROUP BY c1
)
SELECT lst1.c1 c1a,
lst2.c1 c1b
FROM lst_dist lst1
INNER JOIN lst_dist lst2
ON lst1.c2_lst = lst2.c2_lst
AND lst1.c1 < lst2.c1
You can do this without list agg, using a self join:
select t1.c1, t2.c1
from (select t.*, count(*) over (partition by c1) as cnt
from table t
) t1 join
(select t.*, count(*) over (partition by c1) as cnt
from table t
) t2
on t1.c2 = t2.c2 and t1.c1 < t1.c2 and t1.cnt = t2.cnt
group by t1.c1, t2.c1
having count(*) = max(t1.cnt);
Note: this assumes that there are no duplicate rows in the table. A slight variation can work in that case as well.
This joins the rows on the second column and then aggregates by the first. Along the way, it makes sure that the number of matching columns is the same in the two table and that all columns match.