Performance issue with IN clause - oracle

I have question for using IN clause in sql query which of the following provide better performance
SELECT * FROM emp WHERE deptno IN (10,20)
OR
WITH dep AS (SELECT 10 deptno FROM DUAL UNION ALL
SELECT 20 deptno FROM DUAL)
SELECT * FROM EMP e
WHERE EXISTS (SELECT 1 FROM dep WHERE dep.deptno=e.deptno);
I am looking which will provide better performance

"In clause" will be better choice because in another example optimizer can't figure out how to join this two tables so it scans all of the emp table and see if particularly record meets your condition. I've checked this on huge table (more than million rows) and the query plan was very different. Of course I assumed that you have index on deptno column. Without it both solutions require full table scan on emp table.

Related

Calculate number of rows in an Oracle query

Is there a simple way to get the number of rows an Oracle SQL query returns?
I tried count in various ways but none worked. This seems to do the trick
SELECT ROWNUM, mv.*
FROM my_view mv
where col_a IS NOT NULL
order by ROWNUM desc
but is there something straightforward like nrow in R or .shape in Python?
SELECT count(*) over () as row_count, mv.*
FROM my_view mv
where col_a IS NOT NULL
will give you a count but unless you can be sure that performance is not going to be a problem, its generally a bad idea to do this. Because what if the table has 1 billion rows and you only need to show the first (say) 200 rows on screen? We are going to visit all the candidate rows in order to work out that count.
That's why a Google search says "Results 1..20 of ABOUT ..."
I was just wondering what the best practice would be to check the size of the view (...) I don't need to display the entire view, but only need to know the number of rows.
In that case, count(*) seems to be the way to do it. Pure & simple
SELECT count(*)
FROM my_view
WHERE col_a IS NOT NULL;
(You used order by clause; it promises to be slower than query without it.)
Alternatively, if it were a table (not a view), you could use a really fast option - query user_tables:
SQL> SELECT num_rows
2 FROM user_tables
3 WHERE table_name = 'EVID';
NUM_ROWS
----------
808757
How many rows does that table really have?
SQL> SELECT COUNT (*) FROM evid;
COUNT(*)
----------
808761
SQL>
Not exactly the same. Why? Because you should gather statistics:
SQL> EXEC DBMS_STATS.gather_table_stats('SJERV', 'EVID');
PL/SQL procedure successfully completed.
Now those values match:
SQL> SELECT num_rows
2 FROM user_tables
3 WHERE table_name = 'EVID';
NUM_ROWS
----------
808761
SQL>
It just means that you should regularly gather (schema) statistics - for example, on a daily basis. Then you'd have pretty much close information about number of rows. But, to be sure how many rows you really have, once again - select count(*).

how to use 50 thousand Ids at where or join clause in oracle pl/sql for a select query?

I have a list of 50 thousand receipt Ids (hard coded values). i want to apply these 50 thousand Ids in where condition or join operation. I have used below 'with' clause to create a temp table to collect those 50 thousand Ids. Then I used this temp table in join query for filtering.
with temp_receiptIds(receiptId)
as
(
select 'M0000001' from dual
union
select 'M0000002' from dual
union
select 'M0000003' from dual
union
select 'M0000004' from dual
..
..
...
union
select 'M0049999' from dual
union
select 'M0050000' from dual
)
select sal.receiptId, prd.product_name, prd.product_price, sal.sales_date, sal.seller_name
from product prd
join sales sal on prd.product_id=sal.product_id
join temp_receiptIds tmp on tmp.receiptId=sal.receiptId
Whenever I run the above select join query to extract data as requested by business people, it takes about 8 minutes to fetch result in the production server.
Is my above approach correct? Are there any simpler approach than this by considering best performance in the production server.
Please note, every second , the production database is used by customer. since production db is very busy, can I run this query in production db directly, will it cause slow performance in the customer using website which calls this production db in every second. Correct answers would be greatly appreciated! Thanks
Why wouldn't you store those receiptIDs into a table?
create table receiptids as
with temp_receiptIds(receiptId)
as
(
select 'M0000001' from dual
union all --> "union ALL" instead of "union"
...
)
select * from temp_receiptids;
Index it:
create index i1recid on receiptids (receiptIdD);
See how that query now behaves.
If you - for some reason - can't do that, see whether UNION ALL within the CTE does any good. For 50.000 rows, it could make a difference.

HAVING clause without GROUP BY in Oracle database using developer desktop and developer web

My understanding as per standard practice is that HAVING is to be used along with GROUP BY for filtering conditions, while WHERE is supposed to be used for general row-wise filtering conditions.
However, there are online discussions with mixed conclusions as to whether use HAVING as a superset of WHERE clause. That is, whether it can be used even without GROUP BY in which case it works as a WHERE clause.
I want to understand what is the industry practice in using HAVING clause across Oracle, Microsoft SQL server, MySQL, PostGreSQL and other tools.
A funny thing I observed when executing this query:
SELECT *
FROM SH.SALES
WHERE amount_sold > 1000
HAVING amount_sold < 2000;
It gives an error when executing in Oracle SQL developer desktop whereas runs successfully in Oracle SQL developer web.
This is a great question AND puzzle!
Oracle SQL Developer Web is provided via Oracle REST Data Services (ORDS). There is a RESTful Web Service used to execute 'ad hoc' SQL statements and scripts.
Instead of bringing back all the rows from a query in a single call, we page them. And instead of holding a resultset open and process running, we stick to the RESTful way, and do all the work on a single call and response.
How do we make this happen?
Well, when you type in that query from your question and execute it, on the back end, that's not actually what gets executed.
We wrap that query with another SELECT, and use the ROW_NUMBER() OVER analytic function call. This allows us to 'window' the query results, in this case between rows 1 and 26, or the the first 25 rows of that query, your query.
SELECT *
FROM (
SELECT Q_.*,
ROW_NUMBER() OVER(
ORDER BY 1
) RN___
FROM (
select *
from sh.sales
where amount_sold > 1000
having amount_sold < 2000
) Q_
)
WHERE RN___ BETWEEN :1 AND :2
Ok, but so what?
Well, Optimizer figures out this query can still run, even if the having clause isn't appropriate.
The optimizer is always free to re-arrange a query before searching for best execution plans.
In this case, a 10053 trace shows that a query such as below that came from SQL Dev Web (I'm using EMP but the same applies for any table)
SELECT *
FROM (
SELECT Q_.*,
ROW_NUMBER() OVER(
ORDER BY 1
) RN___
FROM (
SELECT *
FROM emp
WHERE sal > 1000
HAVING sal < 2000
) Q_
)
WHERE RN___ BETWEEN :1 AND :2
got internally transformed to the following before being optimized for plans.
SELECT
subq.EMPNO EMPNO,
subq.ENAME ENAME,
subq.JOB JOB,
subq.MGR MGR,
subq.HIREDATE HIREDATE,
subq.SAL SAL,subq.COMM COMM,
subq.DEPTNO DEPTNO,
subq.RN___ RN___
FROM
(SELECT
EMP.EMPNO EMPNO,
EMP.ENAME ENAME,
EMP.JOB JOB,EMP.MGR MGR,
EMP.HIREDATE HIREDATE,
EMP.SAL SAL,
EMP.COMM COMM,
EMP.DEPTNO DEPTNO,
ROW_NUMBER() OVER ( ORDER BY NULL ) RN___
FROM EMP EMP
WHERE EMP.SAL>1000 AND TO_NUMBER(:B1)>=TO_NUMBER(:B2)
) subq
WHERE subq.RN___>=TO_NUMBER(:B3)
AND subq.RN___<=TO_NUMBER(:B4)
Notice the HAVING has been transformed/optimized out of the query, which lets it pass through onto the execution phase.
Major 👏 to #connor-mcdonald of AskTom fame for helping me parse this out.
And so that's why it works in SQL Developer Web, but NOT in SQL Developer Desktop, where the query is executed exactly as written.

How to combine multiple rows in Oracle SQL (having multiple limitations)

I need to combine multiple rows in Oracle SQL but have no access to LISTAGG or wm_concat (EVALUATE_AGGR disabled).
Note: I need this to work in Oracle OBIEE 11.1.1.9.
Grateful for any help or tips at all.
Ugh. I hate writing this as an Answer, but I found that the sys_connect_by_path solutions, both at Oracle-Base (see Alex Poole's comment) and on William Robertson's web site (quoted in the Oracle-Base article), are less than perfect, and this won't fit in a comment.
Oracle-Base link: https://oracle-base.com/articles/misc/string-aggregation-techniques#row_number
William Robertson web site: http://www.williamrobertson.net/documents/one-row.html
The solution on Oracle-Base uses two calls to row_number() when only one is needed, and it uses an aggregate query instead of connect_by_isleaf. Perhaps that's the solution originally posted by William, but his page currently has the better solution, using just one row_number() call and connect_by_isleaf instead of aggregation.
However, on William's page, he uses ltrim() without the argument that shows which character to trim, so in fact it has no effect. And he subtracts 1 from the value of row_number(), so in the result the first token in each comma-separated list is left out.
Here is the corrected solution - for reference; I claim no originality to any of this. The illustration is run on the EMP table in the standard SCOTT schema.
select deptno
, ltrim(sys_connect_by_path(ename,','), ',') as name_list
from ( select deptno
, ename
, row_number() over (partition by deptno order by ename) as seq
from emp )
where connect_by_isleaf = 1
connect by seq = prior seq + 1 and deptno = prior deptno
start with seq = 1;
DEPTNO NAME_LIST
------ ------------------------------------
10 CLARK,KING,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

Oracle Bulk Collect and Update from cursor or user Update with Select subquery

I have 2 options to update a table based on a cursor of a select query.
Say I have this select query:
select id1 from table1
and my update query is :
update table2 set value=1 where table2.id2 = table1.id1
Now the 2 options are:
set a cursor the select query and fetch it in bulk and then fire the update query inside the for all statement.
write update query with the select subquery as:
update table2 set value=1 where table2.id2 in (select id1 from table1)
which one is better?
Does Oracle internally convert the select subquery in to a bulk collect or does it treat it as a normal cursor?
First on your question "Does Oracle internally convert the select subquery in to a bulk collect?"
No. The optimizer calculates a plan and selects the data from the subquery in some appropiate way. There's no bulk collect involved.
To your question "which one is better?". Well, it depends. If you can formulate a query which gets all of your table1.id1 in one run and table2 has lots of rows so the subselect is expensive, then I'd probably use bulk collect. But keep in mind that depending on the amount of data, you'll need some PGA to accomplish this.
But I may point to another - IMHO quite elegant - solution:
MERGE INTO table2
USING (select id1 from table1)
ON (id2 = id2)
WHEN MATCHED THEN
UPDATE SET value=1
;
That's usually faster than doing subqueries and faster than bulk collects: one run on table1 and one run on table2. (Add where clause to your needs)

Resources