Jooq resolves unnest / table to union all (Oracle) - oracle

I'm using jOOQ to delete a variable number of rows from an Oracle database:
List<Integer> ids = Lists.newArrayList(1, 2, 3, 4);
db.deleteFrom(MESSAGE)
.where(MESSAGE.ID.in(ids))
.execute();
However, this means that a variable number of bind variables is used. This leads to the problem that Oracle always does a hard parse.
I have tried using the unnest or table function to create a statement with only one bind variable. Unfortunately, this does not seem to work. jOOQ creates statements with multiple bind variables and union all statements:
db.deleteFrom(MESSAGE)
.where(MESSAGE.ID.in(
select(field("*", Long.class))
.from(table(ids))
))
.execute();
LoggerListener DEBUG - Executing query : delete from "MESSAGE" where "MESSAGE"."ID" in (select * from ((select ? "COLUMN_VALUE" from dual) union all (select ? "COLUMN_VALUE" from dual) union all (select ? "COLUMN_VALUE" from dual) union all (select ? "COLUMN_VALUE" from dual)) "array_table")
LoggerListener DEBUG - -> with bind values : delete from "MESSAGE" where "MESSAGE"."ID" in (select * from ((select 1 "COLUMN_VALUE" from dual) union all (select 2 "COLUMN_VALUE" from dual) union all (select 3 "COLUMN_VALUE" from dual) union all (select 4 "COLUMN_VALUE" from dual)) "array_table")
The javadoc of the unnest function recommends using the table function for Oracle
Create a table from an array of values.
This is equivalent to the TABLE function for H2, or the UNNEST function in HSQLDB and Postgres
For Oracle, use table(ArrayRecord) instead, as Oracle knows only typed arrays
In all other dialects, unnesting of arrays is emulated using several UNION ALL connected subqueries.
Altough I use the table function, I still get the emulated UNION ALL statement.

TABLE operator specifics
Oracle's TABLE(collection) function doesn't accept arbitrary arrays, only SQL table types, such as:
CREATE TYPE t AS TABLE OF NUMBER
You could create such a type and use the code generator to create a TRecord, which you could pass to that TABLE operator:
from(table(new TRecord(ids)))
Unfortunately, such a type is required in Oracle. In its absence, the UNION ALL emulation seems to be the best way, but that doesn't solve your problem.
Note the TABLE operator has its own caveats, mainly that it produces poor cardinality estimates, so for small tables, it may not be the best choice. Though, do measure yourself! I've documented this here in a blog post
Avoiding cursor cache contention
jOOQ has a nice feature called IN list padding, which mitigates most of the dynamic SQL problems that arise from such hard parses. In short, it prevents arbitrary IN list sizes by repeating the last element to pad the list up to 2^n elements, getting you:
id IN (?) -- For 1 element
id IN (?, ?) -- For 2 elements
id IN (?, ?, ?, ?) -- For 3-4 elements
id IN (?, ?, ?, ?, ?, ?, ?, ?) -- For 5-8 elements
It's a hack for when the TABLE operator is too complex. It works by reducing the number of possible predicates logarithmically, which might just be good enough.
Other alternatives
You can always also use:
Temp tables, and batch insert the IDs in there
Use an actual semi join: ID IN (SELECT original_id FROM original_table) (this is usually the best approach, if possible)

Related

How to user app_user in the where condition

using v('APP_USER') in where condition is returning to null records
select FULL_NAME, EMAIL_ADDRESS from headcount where EMAIL_ADDRESS = v('APP_USER')
There are 3 ways to get the value of APP_USER in an apex session. The preferred way is the bind variable (:APP_USER) but you can you also use the sys_context variable SYS_CONTEXT('APEX$SESSION','APP_USER') or the V function (V('APP_USER')). To prove that works I created a report with the following query:
WITH app_user_tab AS (SELECT 'bind var' as type, :APP_USER as username FROM DUAL
UNION
SELECT 'context',SYS_CONTEXT('APEX$SESSION','APP_USER') FROM DUAL
UNION
SELECT 'v', v('APP_USER') FROM DUAL)
SELECT * FROM app_user_tab
And i got 3 identical rows as expected.
Which leads me to believe your problem is not with the app user, but with the value in headcount.email_address. It could be:
case not matched.
leading/trailing control characters in the headcount.email_address column
you have to use bind variables in your sql.
V / NV functions are used in compiled code, such as views, packages, and procedures.
more info are here

Performance: XMLDom vs XMLTable vs Extractvalue()

In Oracle, which one is faster in terms of accessing a single value ?
xmldom.getNodeValue(xmlNode);
Or
select extractvalue( XMLType('<nodeName>...'), '/nodeName/...') from dual
Or XMLTable?
Select *
From XMLTABLE ('nodeName',
XmlType('<nodeName>)
Columns — that 1 value);

Regarding Oracle query functional behaviour

CREATE TABLE ak_temp
(
misc_1 varchar2(4000),
misc_2 varchar2(4000),
time_of_insert timestamp
);
Query 1(Original) :
select *
from ak_temp
where misc_1 in('ankush')
or misc_2 in('ankush')
Query 2 :
select *
from ak_temp
where 'ankush' in (misc_1,misc_2)
Hi, I have somewhat similar problem with the queries, i want to avoid Query 1 since the cost in our live environment is coming bit on higher side so i have figured out Query 2 with less cost, are these both functionally equivalent?
The two are functionally equivalent and should produce the same query plan, so one should not have a performance advantage over the other (or any such advantage would be miniscule). Oracle might be smart enough to take advantage of two indexes, one on ak_temp(misc_1) and one on ak_temp(misc_2).
However, you might also consider:
select t.*
from ak_temp t
where misc_1 = 'ankush'
union
select t.*
from ak_temp t
where misc_2 = 'ankush';
This version will definitely take advantage of indexes, but the union can slow things down if many rows match the two conditions.
EDIT:
To avoid the union, you can do:
select t.*
from ak_temp t
where misc_1 = 'ankush'
union all
select t.*
from ak_temp t
where misc_2 = 'ankush' and (misc_1 <> 'ankush' and misc_1 is not null); -- or use `lnnvl()`

Is order preserved in UNION ALL using LAST_VALUE?

Hello all :) I have this query in Oracle 10g:
SELECT
LAST_VALUE(SERIAL_ID) OVER (),
LAST_VALUE(COLOR) OVER ()
FROM (
SELECT SERIAL_ID, COLOR FROM TABLE_1
UNION ALL
SELECT SERIAL_ID, COLOR FROM TABLE_2
) WHERE SERIAL_ID = <PUT UNIQUE ID TO TABLE_1 and TABLE_2 HERE>
TABLE_1 and TABLE_2 have the exact same schema, but different data. There is a unique constraint on SERIAL_ID, but one SERIAL_ID can be found in both tables.
So far the LAST_VALUE(COLOR) OVER () has always returned the value from TABLE_2 over TABLE_1 when there is a match in TABLE_2. Which is what I want.
I cannot find information in the documentation that tells me that the order from UNION ALL will get preserved. In my opinion UNION ALL is from the set realm, and I don't know if Oracle reserves the right to present this set in undefined order.
I want to make sure that the order will stay the same.
Best regards
Unless it's documented it isn't safe to assume anything about ordering, other than Tom Kyte's mantra. Even it appears to always work now, that doesn't mean you won't find it working differently one day, in the current or a future version.
You could ensure this by adding a flag to each branch of the union:
SELECT
LAST_VALUE(SERIAL_ID) OVER (ORDER BY FLAG
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING),
LAST_VALUE(COLOR) OVER (ORDER BY FLAG
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM (
SELECT SERIAL_ID, COLOR, 1 AS FLAG FROM TABLE_1
UNION ALL
SELECT SERIAL_ID, COLOR, 2 AS FLAG FROM TABLE_2
) WHERE SERIAL_ID = <PUT UNIQUE ID TO TABLE_1 and TABLE_2 HERE>
This will work even if you (artifically) break it by adding an ORDER BY FLAG DESC to the inner query - SQL Fiddle showing that in action.
Well, UNION ALL always returned ordered data for me. Order is by all columns, starting from first one. To set records in order you need, you will have to generate some additional field in both internal queries and then order on that field in outer query.

What does PARTITION BY 1 mean?

For a pair of cursors where the total number of rows in the resultset is required immediately after the first FETCH, ( after some trial-and-error ) I came up with the query below
SELECT
col_a,
col_b,
col_c,
COUNT(*) OVER( PARTITION BY 1 ) AS rows_in_result
FROM
myTable JOIN theirTable ON
myTable.col_a = theirTable.col_z
GROUP BY
col_a, col_b, col_c
ORDER BY
col_b
Now when the output of the query is X rows, rows_in_result reflects this accurately.
What does PARTITION BY 1 mean?
I think it probably tells the database to partition the results into pieces of 1-row each
It is an unusual use of PARTITION BY. What it does is put everything into the same partition so that if the query returns 123 rows altogether, then the value of rows_in_result on each row will be 123 (as its alias implies).
It is therefore equivalent to the more concise:
COUNT(*) OVER ()
Databases are quite free to add restrictions to the OVER() clause. Sometimes, either PARTITION BY [...] and/or ORDER BY [...] are mandatory clauses, depending on the aggregate function. PARTITION BY 1 may just be a dummy clause used for syntax integrity. The following two are usually equivalent:
[aggregate function] OVER ()
[aggregate function] OVER (PARTITION BY 1)
Note, though, that Sybase SQL Anywhere and CUBRID interpret this 1 as being a column index reference, similar to what is possible in the ORDER BY [...] clause. This might appear to be a bit surprising as it imposes an evaluation order to the query's projection. In your case, this would then mean that the following are equivalent
COUNT(*) OVER (PARTITION BY 1)
COUNT(*) OVER (PARTITION BY col_a)
This curious deviation from other databases' interpretation allows for referencing more complex grouping expressions.

Resources