Very slow query using FROM DUAL UNION ALL in a CTE - oracle

I'm using Oracle SQL Developer for find the issue with the data that the MERGE statement raises the ORA-30926 error.
ORA-30926: unable to get a stable set of rows in the source tables
The problem I have is that this issue is happening on production server and we don't have insert privileges - we can only query the data.
So, I decide to use CTEs for build the sub-query "with the data the table used by the MERGE statement".
The CTE is as follows:
WITH SAMPLE_TABL
AS
(
SELECT 8 IIY_CODEN, '1016755' NUM_DCSM, 1 RW_NSKD FROM DUAL UNION ALL
SELECT 1 IIY_CODEN, '30029' NUM_DCSM, 1 RW_NSKD FROM DUAL UNION ALL
SELECT 1 IIY_CODEN, '21652019' NUM_DCSM, 1 RW_NSKD FROM DUAL UNION ALL
SELECT 1 IIY_CODEN, '10038' NUM_DCSM, 1 RW_NSKD FROM DUAL UNION ALL
SELECT 1 IIY_CODEN, '110004567448' NUM_DCSM, 1 RW_NSKD FROM DUAL
[...] -- 32706 rows
)
SELECT T1.*
FROM SAMPLE_TABL T1;
I'm constructing this sub-query for emulate the content of a table
that is used on a MERGE statement that raises the ORA-30926 error.
With this result of the sub-query, I then run the SELECT that is
used on the MERGE statement and export such results. Then, I can
check which are the duplicated rows.
The SELECT used in the MERGE has already a DISTINCT keyword on
it, so, I'm narrow it down to steps for see the data that is executed
on that specific MERGE.
The information that is used on the CTE was supplied by the customer
on a Excel file, so, I had to create the CTE in this way.
NOTE: If anyone know alternative(s) about how this issue can be handled, I'm open to suggestions.
This CTE has 32706 rows and, testing in a different database, the query completes after 12 minutes.
I tried this query on production and I have to cancel the query after 24 minutes and the cancellation took another more minutes.
I suspect this "result" is due to oracle/sql developer limitations in GUI or probably the database engine itself, but, I really don't know how then I can have a running sub-query with these 32K rows in order to check further the cause of the ORA-30926 error.
These are the details of the SQL Developer instance I'm using - and I know that according to this comment, this SQL Developer version is very old, but, that's what I got for work.
About
---------
Oracle SQL Developer 17.2.0.188
Versión 17.2.0.188
Versión Interna 188.1159
IDE Version: 13.0.0.1.42.170225.0201
Product ID: oracle.sqldeveloper
Product Version: 17.2.0.188.1159
Versión
-------
Componente Versión
========== =======
Oracle IDE 17.2.0.188.1159
Plataforma Java(TM) 1.8.0_131
Soporte de Control de Versiones 17.2.0.188.1159
I narrow this question as down to:
How I can have a running and working sub-query with 32K rows?

Related

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.

Strange Behaviour Related to ORA-01400 Error [duplicate]

Oracle 12 introduced nice feature (which should have been there long ago btw!) - identity columns. So here's a script:
CREATE TABLE test (
a INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
b VARCHAR2(10)
);
-- Ok
INSERT INTO test (b) VALUES ('x');
-- Ok
INSERT INTO test (b)
SELECT 'y' FROM dual;
-- Fails
INSERT INTO test (b)
SELECT 'z' FROM dual UNION ALL SELECT 'zz' FROM DUAL;
First two inserts run without issues providing values for 'a' of 1 and 2. But the third one fails with ORA-01400: cannot insert NULL into ("DEV"."TEST"."A"). Why did this happen? A bug? Nothing like this is mentioned in the documentation part about identity column restrictions. Or am I just doing something wrong?
I believe the below query works, i havent tested!
INSERT INTO Test (b)
SELECT * FROM
(
SELECT 'z' FROM dual
UNION ALL
SELECT 'zz' FROM dual
);
Not sure, if it helps you any way.
For, GENERATED ALWAYS AS IDENTITY Oracle internally uses a Sequence only. And the options on general Sequence applies on this as well.
NEXTVAL is used to fetch the next available sequence, and obviously it is a pseudocolumn.
The below is from Oracle
You cannot use CURRVAL and NEXTVAL in the following constructs:
A subquery in a DELETE, SELECT, or UPDATE statement
A query of a view or of a materialized view
A SELECT statement with the DISTINCT operator
A SELECT statement with a GROUP BY clause or ORDER BY clause
A SELECT statement that is combined with another SELECT statement with the UNION, INTERSECT, or MINUS set operator
The WHERE clause of a SELECT statement
DEFAULT value of a column in a CREATE TABLE or ALTER TABLE statement
The condition of a CHECK constraint
The subquery and SET operations rule above should answer your Question.
And for the reason for NULL, when pseudocolumn(eg. NEXTVAL) is used with a SET operation or any other rules mentioned above, the output is NULL, as Oracle couldnt extract them in effect with combining multiple selects.
Let us see the below query,
select rownum from dual
union all
select rownum from dual
the result is
ROWNUM
1
1

Internal Functionality of DUAL table?

Will local database get disturb if we create DUAL table ?
Kindly Suggest me ?
create table DUAL
(
x varchar2(1)
);
No you cannot create a dual table. DUAL table is owned by SYS and SYS owns the data dictionary so you can not create it.
See the wiki
The DUAL table is a special one-row table present by default in all
Oracle database installations. It is suitable for use in selecting a
pseudocolumn such as SYSDATE or USER. The table has a single
VARCHAR2(1) column called DUMMY that has a value of 'X'.
Even if you try to create a DUAL table then it will create problems for you as everytime the Oracle engine has to ensure that you are not calling the SYS dual table. You need to specify the database and schema as well. It may lead to too much of ambiguity problem for Oracle engine. The Oracle optimizer knows everything that DUAL does and what it should do and it then does things based on that.
SQL Reference:
DUAL is a table automatically created by Oracle Database along with
the data dictionary. DUAL is in the schema of the user SYS but is
accessible by the name DUAL to all users. It has one column, DUMMY,
defined to be VARCHAR2(1), and contains one row with a value X.
Selecting from the DUAL table is useful for computing a constant
expression with the SELECT statement. Because DUAL has only one row,
the constant is returned only once. Alternatively, you can select a
constant, pseudocolumn, or expression from any table, but the value
will be returned as many times as there are rows in the table. Refer
to "About SQL Functions" for many examples of selecting a constant
value from DUAL.
Beginning with Oracle Database 10g Release 1, logical I/O is not
performed on the DUAL table when computing an expression that does not
include the DUMMY column. This optimization is listed as FAST DUAL in
the execution plan. If you SELECT the DUMMY column from DUAL, then
this optimization does not take place and logical I/O occurs.
Will local database get disturb if we create DUAL table ?
Yes, of course weird things can and will happen. DUAL is owned by SYS. SYS owns the data dictionary, therefore DUAL is part of the data dictionary. You are not to modify the data dictionary via SQL ever.
And the first question is "How will you guarantee only one row in your own DUAL table"?
This goes back to the original article Self-Managing PL/SQL by Steven Feuerstein where he explains "Use Your Own DUAL Table". But, that was back then when DUAL table was prone to such things.
However, in the recent releases, the DUAL table structure has been made robust and you cannot have more than single row ever. Here is a proof:
SQL> conn sys#pdborcl as sysdba
Enter password:
Connected.
SQL> insert into dual select * from dual;
1 row created.
SQL> select * from dual;
D
-
X
I know, few would argue that we can handle one row with our own DUAL table using a trigger or ROWNUM =1, however, you will soon realize the cons. It is simply not necessary from 10g on wards, as the DUAL table is now a memory structure and you cannot add a row to it as demonstrated above.
Imagine a situation where you have created your own DUAL table, and you are using the call to DUAL table in your PL/SQL code to get the USER, SYSDATE, SYSTIMESTAMP etc.
This is the code taken from the stdbody.sql file delivered with Oracle Database:
1 FUNCTION USER
2 RETURN VARCHAR2
3 IS
4 c VARCHAR2 (255);
5 BEGIN
6 SELECT USER
7 INTO c
8 FROM SYS.DUAL;
9
10 RETURN c;
11 END;
If you ever have more than one row in your own DUAL table, every call to the USER function in your PL/SQL code will fail with TOO_MANY_ROWS error.
Bottomline : All the discussion about using your own DUAL table made sense back then before 10g days. The DUAL table is now a robust memory structure and doesn't allow to add a row to it. So, makes no sense to use your own DUAL table rather than the SYS.DUAL.

Different results in Parallel Execution - Oracle

In my company's application there is a query in oracle using parallel execution (configured to 4 servers), it wasn't me who built it, but the developer put it that way for performance.
The query makes joins between views and tables and the weirdest thing is: sometimes it returns 11k results (incorrect), sometimes 27k results (correct).
After much research I found out that if I removed this parallel thing, it always returns the correct number: 27k. And if I increase the number of server to 6 or 7, it always returns the incorrect number: 11k.
The layout of the query is like this:
SELECT /*+ PARALLEL(NAME, 4) */ * FROM(
SELECT DISTINCT COLUMNS
FROM VIEW
JOIN TABLE1 ON (....)
JOIN TABLE2 ON (....)
JOIN TABLE3 ON (....)
ORDER BY 3
) NAME
Anyone has any idea why? I don't know much about this subject.

Resources