Using a CTE to update in Oracle - oracle

For logistic reasons, I want to set a value in a CTE to use in an UPDATE statement. Here is a simplified version:
with vars as (select 5 as n from dual)
update test set data=data*vars.n, more=more*vars.n;
Of course, in real life, the CTE will calculate a value from another source, but this should make the point.
I have sample at https://dbfiddle.uk/tUJoX5uw .
I get the error
ORA-00928: missing SELECT keyword
Is it possible to use a CTE this way for an UPDATE statement?
I know about some other SO questions, but generally they either end up using MERGE, or say that you can’t update the CTE itself. I don’t want to update the CTE, just to use the results. This is a question about the UPDATE statement.
Update
I have accepted an answer below. Here is a simplified version of the solution:
UPDATE TEST t
SET (t.data, t.more) = (
SELECT vars.n*t.data, vars.n*t.more FROM (
SELECT 5 AS n FROM dual
) vars
);

This will not work:
UPDATE TEST t SET (t.data, t.more) = (
WITH VARS(n) AS (
select 5 FROM DUAL
)
SELECT v.n * t.data, v.n * t.more
FROM vars v
)
;
SQL Error: ORA-01767: UPDATE ... SET expression must be a subquery
But this works:
UPDATE TEST t SET (t.data, t.more) = (
SELECT (select 5 from dual) * t.data, (select 5 from dual) * t.more
FROM DUAL
)
;

The WITH should be inside the SET
UPDATE test SET data = (
WITH vars(n) AS (
select 5 FROM DUAL
)
SELECT v.n * t.data
FROM vars v
)
;

Related

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.

How to get the row data that include max value in MySQL?

How to get the total row data that has max(reg_count).
When I try this, it gives me only max reg_count value. I want to get the whole row data that has max reg_count in every albumID.(So like this: 4 Bemisal ha 1 1 8) - total 4 rows
SELECT albumID, max(reg_count) as max_count
FROM contentnew
GROUP BY albumID
Please help me!
You don't mention the version of MySQL you are using so I'll assume it's a modern one (8.x). You can use the ROW_NUMBER() window function to identify the row you need.
For example:
select *
from (
select *,
row_number() over(partition by albumID order by reg_count desc) as rn
from contentnew
) x
where rn = 1
In MySQL 5.x you can use correlated subquery:
select *
from contentnew a
where a.reg_count = (
select max(reg_count)
from contentnew b
where b.albumID = a.albumID)
)

Oracle SQL query to do Interpolation In Oracle 12c

Interpolated results
Dear Experts,
I have interpolated values for AddOn correctly ( highlighted in yellow) using the analytic functions regr_slope and regr_intercept with the script below
with CLP_ADDON_CALC_STG (addon_product, term_years, underlying_term_years, addon) as
( SELECT 'CCREPO', 0.5, 5 , 0.0447 FROM DUAL UNION ALL
SELECT 'CCREPO', 0.5, 10 , 0.0516 FROM DUAL UNION ALL
SELECT 'CCREPO', 0.7, 9 , NULL FROM DUAL UNION ALL
SELECT 'CCREPO', 1, 5 , 0.0567 FROM DUAL UNION ALL
SELECT 'CCREPO', 1, 10 , 0.0487 FROM DUAL)
,ordered_input_1 as (
SELECT
i.*,
row_number() over ( partition by addon_product order by term_years) rn
FROM CLP_ADDON_CALC_STG i where (underlying_term_years < 9 or ( term_years < 0.7 and term_years > 0.7))
or (term_years = 0.7 and underlying_term_years = 9)order by term_years, underlying_term_years
-- where i.term_years
)
SELECT
addon_product,
term_years,
underlying_term_years,
addon,
term_years * regr_slope(addon, term_years) over ( partition by addon_product) +
regr_intercept(addon, term_years) over ( partition by addon_product)
as interpolated_value
FROM ordered_input_1
ORDER BY addon_product, term_years;
Now I am in situation where in I have to do interpolation on the interpolated values derived above for which I am not able to arrange the rows in order as required for the above analytic function.
I had referred to this link to get the rows in order ( Linear Interpolation in Oracle with special cases)
But for the next round of interpolation for which I would need the rows in some order
or I would need to a query (on the rows as seen in the image interpolated results ) which would do something like the below formula
((9-5)*0.05364 + (10-9)*0.0463)/(10-5) which would give me 0.052712
Any help would be greatly appreciated.
Thanks in advance,
Murali

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

generate Multiple UUID Oracle

Oracle:-
I have around 850 records in an table, that need to be assigned UUID.
I am using the following query.
select substr(sys_guid(),1,3)||'-'||
substr(sys_guid(),4,4)||'-'||
substr(sys_guid(),8,4)||'-'||
substr(sys_guid(),13)
from (select sys_guid() as mygid from dual)
I need to generate multiple/850 records in one go.
Any suggestions ?
Should I loop over?
If you really need select, use hierarchical query:
SELECT Substr(mygid,1,3)||'-'||
Substr(mygid,4,4)||'-'||
Substr(mygid,8,4)||'-'||
Substr(mygid,12)
FROM (
SELECT Sys_GUID() AS mygid FROM dual
CONNECT BY Level <= :desired_number_of_records
)
But what's wrong with usual update ?
UPDATE your_tab
SET gid_col = (
SELECT Substr(mygid,1,3)||'-'||
Substr(mygid,4,4)||'-'||
Substr(mygid,8,4)||'-'||
Substr(mygid,12)
FROM( SELECT Sys_Guid() AS mygid FROM dual )
)
Not sure that format is really what you want as you are missing 9 of the 32 characters, buy you could modify the format as needed. Here is an example that shows how to format like XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:
UPDATE MY_TABLE
SET GUID_COL = (
select regexp_replace((rawtohex(sys_guid()), '([A-F0-9]{8})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{12})', '\1-\2-\3-\4-\5') as FORMATTED_GUID from dual
)

Resources