I was wondering, can we self join the table/view that we get back from a subquery?
If yes, then how?
I came up with an approach to apply inner join on the same subquery like:
SELECT attributes that you want
FROM (subquery) t1 INNER JOIN (subquery{same as t1}) t2
ON t1.attribue = t2.attribte;
Note: this is a pseudocode
Pretty much easy with a CTE.
This example doesn't make much sense, it just shows how to do it:
SQL> with t1 as
2 (select deptno
3 from dept
4 )
5 select a.*
6 from t1 a inner join t1 b on a.deptno = b.deptno;
DEPTNO
----------
10
20
30
40
SQL>
Related
Just wondering why the following select isn't working:
SELECT
A.FIELD1
, (SELECT PCN FROM (select B.PRIORITY, B.PCN
from
TABLE2 B
WHERE B.CUST= A.CUST
ORDER BY B.PRIORITY)
WHERE ROWNUM = 1) AS PCN
FROM TABLE1 A;
ERROR at line 2: ORA-00904: "A"."CUST": invalid identifier
Important to mention:
TABLE1 has as fields FIELD1, CUST.
TABLE2 has as fields PCN, PRIORITY, CUST.
Thanks in advance.
Your query shouldn't give you that error message, on when you remove the outer qiery this would happen
CREATE tABLE TABLE1 (FIELD1 int, CUST int)
INSERT INTO TABLE1 VALUES(1,1)
1 rows affected
CREATE TABLE TABLE2 (PCN int, PRIORITY int, CUST int)
INSERT INTO TABLE2 VALUES (1,1,1)
1 rows affected
SELECT
A.FIELD1
, (SELECT PCN FROM (select B.PRIORITY, B.PCN
from
TABLE2 B
WHERE B.CUST= A.CUST
ORDER BY B.PRIORITY)
WHERE ROWNUM = 1) AS PCN
FROM TABLE1 A;
FIELD1
PCN
1
1
fiddle
You can't nest inline selects (more than one level) without losing the ability of the inner nested selects being able to reference the parent block. So your query on TABLE2 cannot see the columns from TABLE1 because of this nesting.
Try this:
SELECT a.field1,
pcn.pcn
FROM table1 a,
(SELECT b.cust,
b.priority,
b.pcn,
ROW_NUMBER() OVER (PARTITION BY b.cust ORDER BY b.priority DESC) seq
FROM table2 b) pcn
WHERE a.cust = pcn.cust(+)
AND pcn.seq(+) = 1
That will work well for report queries. If you end up adding a filter on a specific customer, then you would be better off using OUTER APPLY if you have a recent-enough version of Oracle that supports that.
You could try this:
SELECT
A.FIELD1
, (SELECT B.PCN
from
TABLE2 B
WHERE B.CUST= A.CUST
ORDER BY B.PRIORITY
FETCH FIRST 1 ROWS ONLY) AS PCN
FROM TABLE1 A;
FETCH FIRST 1 ROWS ONLY gets you the first ordered record. Works on 12c and up and supports nesting, and no 2nd subquery needed.
Yet another option might be a CTE.
Sample data:
SQL> with
2 table1 (field1, cust) as
3 (select 1, 100 from dual union all
4 select 2, 200 from dual
5 ),
6 table2 (pcn, priority, cust) as
7 (select 10, 1, 100 from dual union all
8 select 20, 2, 100 from dual union all
9 select 30, 1, 200 from dual
10 ),
Query begins here. Rank rows by priority, and then fetch the ones that rank as the highest (line #20):
11 temp as
12 (select a.field1,
13 b.pcn,
14 rank() over (partition by a.field1 order by b.priority desc) rnk
15 from table1 a join table2 b on a.cust = b.cust
16 )
17 select field1,
18 pcn
19 from temp
20 where rnk = 1;
FIELD1 PCN
---------- ----------
1 20
2 30
SQL>
You may use first aggregate function to achieve the same (assuming that you have completely deterministic order by) functionality without nested subquery:
select
a.field1
, (
select max(b.pcn) keep(dense_rank first order by b.priority)
from table2 b
where b.cust = a.cust
) as pcn
from table1 a
which for this sample data
insert into table1 values(1,1);
insert into table1 values(2,2);
insert into table2 values(1,1,1);
insert into table2 values(2,2,1)
returns
FIELD1
PCN
1
1
2
(null)
SQL fiddle
Hi I have two tables EMP and Dept( data as normal emp and dept tables of oracle)
I want to find the number of employees earning more than the avg(sal) of their own dept without use of correlated subquery. I wrote the query as below
SELECT * from emp e JOIN
(SELECT avg(sal) avgsal,deptno
FROM emp group by deptno )avgsal_tab
on e.sal > avgsal_tab.avgsal where e.deptno =avgsal_tab.deptno
order by e.deptno
This gets me the output but how can I rewrite this with a single query without inline query as shown above.
You can use analytic window functions:
SELECT *
from (
SELECT
e.*
,avg(sal)over(partition by deptno) avgsal
from emp e
)
where avgsal>sal
EXPLAIN SELECT S.ITEMID, X.STATUS
FROM
(select itemid,itemdescription from A WHERE parent_itemid IN(SELECT ITEMID FROM A WHERE itemtype='CT') AND itemtype='SK' AND id='02') S,
(select s1.itemid from s1,s2 where s1.itemid=s2.itemid and status='RC' ) X
where S.itemid=X.itemid
It is not correlated subquery that makes problems, but syntax. Not just explain, but explain plan for.
I don't have your tables so I used Scott's (properly joined):
SQL> explain plan for select s.dname, x.ename
2 from (select d.deptno,
3 d.dname
4 from dept d
5 where d.deptno in (select b.deptno
6 from dept b
7 where b.deptno = 10
8 )
9 ) s join
10 (select e.deptno,
11 e.ename
12 from emp e
13 where e.deptno = 10
14 ) x
15 on s.deptno = x.deptno;
Explained.
SQL>
In your case, presuming that everything else is correct,
explain plan for select s.itemid, x.status
from (select a.itemid,
a.itemdescription
from a
where a.parent_itemid in (select b.itemid
from a b
where b.itemtype = 'CT'
)
and a.itemtype = 'SK'
and a.id = '02'
) s join
(select s1.itemid
from s1 join s2 on s1.itemid = s2.itemid
where s1.status = 'RC' --> which table does STATUS belong to?
) x
on s.itemid = x.itemid;
I'd also suggest you to always use table aliases because it is not clear what belongs to which table. For example, status = 'RC' - without alias, it is impossible to know which table has it. If both, you'd have ambiguity.
Finally, write formatted queries. Mess you posted is difficult to read and understand. Most modern GUI tools have built-in formatters; I suggest you use it. Or, use one of online formatters. Or, format it manually. Just try not to do what you're doing now.
The correct syntax for EXPLAIN is:
EXPLAIN PLAN FOR
SELECT...
Also in the 2nd subquery you must select the column status because you want to select it in the outer select:
EXPLAIN PLAN FOR
SELECT S.ITEMID, X.STATUS
FROM
(select itemid,itemdescription from A WHERE parent_itemid IN(SELECT ITEMID FROM A WHERE itemtype='CT') AND itemtype='SK' AND id='02') S,
(select s1.itemid, status from s1,s2 where s1.itemid=s2.itemid and status='RC' ) X
where S.itemid=X.itemid
Also status should be qualified by the table's name just to avoid any ambiguities.
I have created a cursor that has two queries joined with inner join, but query is not compiling their is error at the end of first query but the same query is getting executed without cursor.
cursor data is
select * from
select rid,id, order from table1
inner join
select pid, name, order from table2
on table1.order = table2.order
original query is much bigger and complicated but end result would be this.
Their are compilation errors at the end of first query and those are generic nature, I guess may be syntax for creating a two joined queries is wrong (this is a wild guess though)
Error:
SQL statement ignored //at select word of first query
Missing right parenthesis //at the last word of first query
Example based on Scott's schema:
SELECT should contain column aliases if columns returned by those inline views share the same name; otherwise, you won't know which one you're using
inline views should have their own aliases; basically, that's always a good idea - prefix columns with table aliases, otherwise you'll soon forget which column belongs to which table
SQL> declare
2 cursor data is
3 select a.empno a_empno, b.ename b_ename
4 from (select empno, ename, deptno from emp) a
5 inner join
6 (select empno, ename, deptno from emp) b
7 on a.deptno = b.deptno
8 where rownum < 5;
9 begin
10 for data_r in data loop
11 dbms_output.put_line(data_r.b_ename);
12 end loop;
13 end;
14 /
SMITH
JONES
SCOTT
ADAMS
PL/SQL procedure successfully completed.
SQL>
You have to put your subqueries in parenthesis and add aliases for the subqueries:
cursor data is
select * from
(select rid,id, order from table1) table1
inner join
(select pid, name, order from table2) table2
on table1.order = table2.order
Here is another answer for you, with just small differences and with an example:
CREATE OR REPLACE PROCEDURE p_test(n_test in number)
AS
CURSOR data
IS
SELECT *
FROM
(SELECT rid
, id
, "order" or1
FROM table1) tab1
INNER JOIN
(SELECT pid
, name
, "order" or1
FROM table2 ) tab2
ON tab1.or1 = tab2.or1;
BEGIN
FOR data_i IN data LOOP
DBMS_OUTPUT.PUT_LINE(data_i.rid);
END LOOP;
END p_test;
Here is the DEMO
I need help with my SQL Query I have Two tables that i need to join using a LEFT OUTER JOIN, then i need to create a database view over that particular view. If i run a query on the join to look for name A i need to get that A's latest brand "AP".
Table 1
ID name address
-----------------------
1 A ATL
2 B ATL
TABLE 2
ID PER_ID brand DATEE
--------------------------------------------
1 1 MS 5/19/17:1:00pm
2 1 XB 5/19/17:1:05pm
3 1 AP 5/19/17:2:00pm
4 2 RO 5/19/17:3:00pm
5 2 WE 5/19/17:4:00pm
I tried query a which returns correct result but i get problem 1 when i try to build the database view on top of the join. I tried query b but when i query my view in oracle sql developer i still get all the results but not the latest.
query a:
SELECT * from table_1
left outer join table_2 on table_1.ID = Table_2.PER_ID
AND table_2.DATE = (SELECT MAX(DATE) from table_2 z where z.PER_ID = table_2.PER_ID)
Problem 1
Error report -
ORA-01799: a column may not be outer-joined to a subquery
01799. 00000 - "a column may not be outer-joined to a subquery"
*Cause: <expression>(+) <relop> (<subquery>) is not allowed.
*Action: Either remove the (+) or make a view out of the subquery.
In V6 and before, the (+) was just ignored in this case.
Query 2:
SELECT * from table_1
left outer join(SELECT PER_ID,brand, max(DATEE) from table_2 group by brand,PER_ID) t2 on table_1.ID = t2.PER_ID
Use row_number():
select t1.id, t1.name, t1.address, t2.id as t2_id, t2.brand, t2.datee
from table_1 t1 left outer join
(select t2.*,
row_number() over (partition by per_id order by date desc) as seqnum
from table_2 t2
) t2
on t1.ID = t2.PER_ID and t2.seqnum = 1;
When defining a view, you should be in the habit of listing the columns explicitly.