Oracle Optimizer Unexpected Results - oracle

I have a co worker who wrote the following query. The first one works and the second one does not. Also if you remove the aggregate function from the subquery, it works. The oracle optimizer is doing something weird. Any thoughts? Running in SQL Developer 3.1 against 11.1.0.6.0 64 bit.
This works:
SELECT
a.fd_customer_key
, b.fd_customer_key
, b.counter
FROM FETCH_CUSTOMER a
, (select fd_customer_key, count(*) as counter from fetch_customer_order group by fd_customer_key) b
where a.fd_customer_key = b.fd_customer_key (+)
and b.counter is null
This doesn’t:
SELECT
a.fd_customer_key
, b.fd_customer_key
, b.counter
FROM FETCH_CUSTOMER a
, (select fd_customer_key, count(*) as counter from fetch_customer_order group by fd_customer_key) b
where a.fd_customer_key = b.fd_customer_key (+)
and b.fd_customer_key is null

Actually yes, both of the queries you provided are supposed to wrok the same way, but if i understand your need well, you are trying to select the fd_customer_key which has no Order?
I suggest the following query for your need, its more simple and less consuming :
SELECT a.fd_customer_key
FROM FETCH_CUSTOMER a
WHERE NOT EXISTS (SELECT 1
FROM fetch_customer_order b
WHERE a.fd_customer_key = b.fd_customer_key)

It seems like you are trying to make an anti-join (find the rows from FETCH_CUSTOMER that have no corresponding rows in FETCH_CUSTOMER_ORDER).
With Oracle you do not have to use this clever OUTER JOIN trick to write an anti-join, you could use a NOT IN or NOT EXISTS operator and let the optimizer find the best plan. This will be just as efficient and easier to read.
Anyway, I can't reproduce your findings, here's my setup:
CREATE TABLE a (ID NUMBER PRIMARY KEY);
CREATE TABLE b (a_id NUMBER NOT NULL, DATA VARCHAR2(30));
INSERT INTO a (SELECT object_id FROM all_objects);
INSERT INTO b (SELECT object_id, object_name
FROM all_objects WHERE object_type = 'VIEW');
SELECT a.id, b.a_id, b.cnt
FROM a, (SELECT a_id, COUNT(*) cnt FROM b GROUP BY a_id) b
WHERE a.id = b.a_id (+)
AND b.cnt IS NULL;
SELECT a.id, b.a_id, b.cnt
FROM a, (SELECT a_id, COUNT(*) cnt FROM b GROUP BY a_id) b
WHERE a.id = b.a_id (+)
AND b.a_id IS NULL;
You will find that both queries return rows. What is your DB version?

Related

how to select specific columns from three different tables in Oracle SQL

I am trying to select values from three different tables.
When I select all columns it works well, but if I select specific column, the SQL Error [42000]: JDBC-8027:Column name is ambiguous. appear.
this is the query that selected all that works well
SELECT
*
FROM (SELECT x.*, B.*,C.* , COUNT(*) OVER (PARTITION BY x.POLICY_NO) policy_no_count
FROM YIP.YOUTH_POLICY x
LEFT JOIN
YIP.YOUTH_POLICY_AREA B
ON x.POLICY_NO = B.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY C
ON B.SMALL_CATEGORY_SID = C.SMALL_CATEGORY_SID
ORDER BY x.POLICY_NO);
and this is the error query
SELECT DISTINCT
x.POLICY_NO,
x.POLICY_TITLE,
policy_no_count ,
B.SMALL_CATEGORY_SID,
C.SMALL_CATEGORY_TITLE
FROM (SELECT x.*, B.*,C.* , COUNT(*) OVER (PARTITION BY x.POLICY_NO) policy_no_count
FROM YIP.YOUTH_POLICY x
LEFT JOIN
YIP.YOUTH_POLICY_AREA B
ON x.POLICY_NO = B.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY C
ON B.SMALL_CATEGORY_SID = C.SMALL_CATEGORY_SID
ORDER BY x.POLICY_NO);
I am trying to select if A.POLICY_NO values duplicate rows more than 18, want to change C.SMALL_CATEGORY_TITLE values to "ZZ" and also want to cahge B.SMALL_CATEGORY_SID values to null.
that is why make 2 select in query like this
SELECT DISTINCT
x.POLICY_NO,
CASE WHEN (policy_no_count > 17) THEN 'ZZ' ELSE C.SMALL_CATEGORY_TITLE END AS C.SMALL_CATEGORY_TITLE,
CASE WHEN (policy_no_count > 17) THEN NULL ELSE B.SMALL_CATEGORY_SID END AS B.SMALL_CATEGORY_SID,
x.POLICY_TITLE
FROM (SELECT x.*, B.*,C.* , COUNT(*) OVER (PARTITION BY x.POLICY_NO) policy_no_count
FROM YIP.YOUTH_POLICY x
LEFT JOIN
YIP.YOUTH_POLICY_AREA B
ON x.POLICY_NO = B.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY C
ON B.SMALL_CATEGORY_SID = C.SMALL_CATEGORY_SID
ORDER BY x.POLICY_NO);
If i use that query, I got SQL Error [42000]: JDBC-8006:Missing FROM keyword. ¶at line 3, column 80 of null error..
I know I should solve it step by step. Is there any way to select specific columns?
That's most probably because of SELECT x.*, B.*,C.* - avoid asterisks - explicitly name all columns you need, and then pay attention to possible duplicate column names; if you have them, use column aliases.
For example, if that select (which is in a subquery) evaluates to
select x.id, x.name, b.id, b.name
then outer query doesn't know which id you want as two columns are named id (and also two names), so you'd have to
select x.id as x_id,
x.name as x_name,
b.id as b_id,
b.name as b_name
from ...
and - in outer query - select not just id, but e.g. x_id.

I am getting "missing keyword" error in co-related sub query in Oracle

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.

Oracle's SDO_CONTAINS not using spatial index on unioned tables?

I'm trying to use Oracle's sdo_contains spatial operator, but it seems, that it's not really working, when you use it on unioned tables.
The below code runs in 2 mins, but you have to duplicate the spatial operator for every source table:
SELECT -- works
x.code,
count(x.my_id) cnt
FROM (select
c.code,
t.my_id
from my_poi_table_1 t,my_shape c
WHERE SDO_contains(c.shape,
sdo_geometry(2001,null,SDO_POINT_type(t.latitude, t.longitude,null),null,null)
) = 'TRUE'
union all
select
c.code,
t.my_id
from my_poi_table_2 t,my_shape c
where SDO_contains(c.shape,
sdo_geometry(2001,null,SDO_POINT_type(t.lat, t.lng,null),null,null)
) = 'TRUE'
) x
group by x.code
I wanted to make it simple, so I tried to first create the points, and then just once use the sdo_contains on it, but it's running for more then 25 mins, because it's not using the spatial index:
SELECT -- does not work
c.code,
count(x.my_id) cnt
FROM my_shape c,
(select
my_id,
sdo_geometry(2001,null,SDO_POINT_type(latitude, longitude,null),null,null) point
from my_poi_table_1 t
union all
select
my_id2,
sdo_geometry(2001,null,SDO_POINT_type(lat, lng,null),null,null) point
from my_poi_table_2 t
) x
WHERE SDO_contains(c.shape,
x.point
) = 'TRUE'
group by c.code
Is there a way to use the sdo_contains on the results of multiple tables without having to include it in the select several times?
Oracle: 12.1.0.2
It seems, that sdo_contains cannot (efficiently) read from a subselect: if I put one of the poi tables into a subselect, then Oracle will not use spatial index for that part:
SELECT -- does not work
x.code,
count(x.my_id) cnt
FROM (select --+ ordered index(c,INDEX_NAME)
c.code,
t.my_id
from my_shape c,(select t.*,rownum rn from my_poi_table_1 t) t
WHERE SDO_contains(c.shape,
sdo_geometry(2001,null,SDO_POINT_type(t.latitude, t.longitude,null),null,null)
) = 'TRUE'
union all
select
c.code,
t.my_id
from my_poi_table_2 t,my_shape c
where SDO_contains(c.shape,
sdo_geometry(2001,null,SDO_POINT_type(t.lat, t.lng,null),null,null)
) = 'TRUE'
) x
group by x.code

How to write multiple sub-query in Hibernate criteria

I am trying to write oracle subquery in Hibernate criteria but unable to do it. Can anyone help me to achieve this. Below is my oracle query.
SELECT a.id,
b.address
FROM tableA a
INNER JOIN TABLE b
ON a.id = b.id
WHERE mainId IN
(SELECT bp.ptyID
FROM bpTable bp,
busHeaderbh bh
WHERE bh.aid = bp.aid
AND bh.parentBID IN
(SELECT bp.ptyID
FROM bpTable bp,
busHeaderbh bh
WHERE bh.aid = bp.aid
AND bh.parentBID = 123
UNION
SELECT 123 FROM dual
)
UNION
SELECT 123 FROM dual
)
AND
GROUP BY a.id,
b.credttm
ORDER BY a.id DESC;
Thanks in Advance.
I have written one example for one to many relationship table
you can get reference from it
Criteria person = session.getCurrentSession().createCriteria(Person.class).createAlias("personId", "personId");
person.add(Restrictions.disjunction().add(Restrictions.ilike("PersonFirstname",Search,MatchMode.ANYWHERE))
.add(Restrictions.ilike("personId.prop1",Search,MatchMode.ANYWHERE))
.add(Restrictions.ilike("personId.col1",Search,MatchMode.ANYWHERE))
.addOrder(Property.forName("colName").desc()
.addOrder(Property.forName("colName").asc());

Oracle select random rows matching a join condition

My objective is simple, I have to create a temporary table with some random values from a employee table whenever the department is in some particular department (say 2). For the rest of departments I don't care the value, it can be NULL.
Currently I have the following :
create table test
as
select s.DEPTNAME,
cast (
(case when s.DEPTID in (2) then
(SELECT a.ENAME FROM
(SELECT b.ENAME, b.DEPTID FROM EMPLOYEE b
WHERE b.DEPTID IS NOT NULL
ORDER BY DBMS_RANDOM.VALUE) a
WHERE a.DEPTID = s.DEPTID AND ROWNUM = 1
)
END)
AS VARCHAR2(30)) "ENAME" from DEPARTMENT s;
But the main issue here is related to performance. For every department value in 2 we do a sort of EMPLOYEE table to get a single random ENAME.
Is there a better way to do this ? I know sample might work but I want to achieve more randomness.
First idea - join randomly numbered enames:
with
e as (select ename, deptid, row_number() over (order by dbms_random.value) rn
from employee where deptid = 2),
c as (select count(1) cnt from e),
d as (select deptname, deptid, round(dbms_random.value(1, c.cnt)) rn from department, c)
select d.deptname, e.ename from d left join e using (rn, deptid)
SQLFiddle demo
Second possible solution, which worked for me, is to create function returning random ename from table employee
and use it in your query, but it would be probably slower.
Edit - according to comment:
If, for some reason, the first part of your statement is "fixed", then you could use this syntax:
create table test as
select deptname, ename from (
with
e as (select ename, deptid, row_number() over (order by dbms_random.value) rn
from employee where deptid = 2),
c as (select count(1) cnt from e),
d as (select deptname, deptid, round(dbms_random.value(1, c.cnt)) rn
from department cross join c)
select d.deptname, e.ename from d left join e using (rn, deptid));

Resources