Oracle JSON_QUERY with path as query column value - oracle

I try to get part of JSON column in each result row this select
SELECT TRIM(a.symbol),
TRIM(a.ex_name),
to_char(a.date_rw, 'dd-MON-yyyy'),
a.pwr,
a.last,
JSON_QUERY(b.mval, '$."-9"') as value
FROM adviser_log a
INNER JOIN profit_model_d b
ON a.date_rw = b.date_rw
WHERE a.date_rw = '08-OCT-2021'
select result:
VERY NAS 08-OCT-2021 -9 8.9443 {"sl":-3.6,"tp":5,"avg":1.368,"max":5,"min":-3.6,"count":1}
As a json path I put "-9" literal but I wanna put as path a.pwr is it possible
I tried put CONCAT('$.', a.pwr) without result
Is it any way to create dynamical json path into JSON_QUERy
I want to match part json which key compared with a.pwr to each row in select
Thx

You can use a function to dynamically get the JSON value:
WITH FUNCTION get_value(
value IN CLOB,
path IN VARCHAR2
) RETURN VARCHAR2
IS
BEGIN
RETURN JSON_OBJECT_T( value ).get_object( path ).to_string();
END;
SELECT TRIM(a.symbol) AS symbol,
TRIM(a.ex_name) AS ex_name,
to_char(a.date_rw, 'dd-MON-yyyy') AS date_rw,
a.pwr,
a.last,
get_value(b.mval, a.pwr) AS value
FROM adviser_log a
INNER JOIN profit_model_d b
ON a.date_rw = b.date_rw
WHERE a.date_rw = DATE '2021-10-08'
Which, for your sample data:
CREATE TABLE adviser_log (symbol, ex_name, date_rw, pwr, last) AS
SELECT 'VERY', 'NAS', DATE '2021-10-08', -9, 8.9443 FROM DUAL;
CREATE TABLE profit_model_d (date_rw DATE, mval CLOB CHECK (mval IS JSON));
INSERT INTO profit_model_d (
date_rw,
mval
) VALUES (
DATE '2021-10-08',
'{"-9":{"sl":-3.6,"tp":5,"avg":1.368,"max":5,"min":-3.6,"count":1}}'
);
Outputs:
SYMBOL
EX_NAME
DATE_RW
PWR
LAST
VALUE
VERY
NAS
08-OCT-2021
-9
8.9443
{"sl":-3.6,"tp":5,"avg":1.368,"max":5,"min":-3.6,"count":1}
db<>fiddle here

Related

alias required in select list of cursor

I m getting an error as when I compiled the below code as alias required in select list of the cursor.
Create Or Replace PROCEDURE pr_no_debit is
Cursor c_Today(From_date date, To_Date date) is
Select Today from sttm_dates where today between From_Date and To_Date;
cursor c_no_debit is
Select a.* , b.* from STTM_NO_DEBIT_customer a , STTM_FIN_CYCLE b where a.Fin_Cycle = b.Fin_Cycle ;
l_No_Debit_List STTM_NO_DEBIT_CUSTOMER%ROWTYPE;
begin
For i_indx in c_Today(l_No_Debit_List.From_Date,l_No_Debit_List.To_Date)
Loop
for j_indx in c_no_debit
loop
update sttm_cust_account set ac_stat_no_Dr='Y' where account_class=j_index.account_class;
end loop;
End Loop;
-- At the end of the period Change No_Debit to 'N'
End pr_no_debit;
Another solution could be to split the cursor into two parts, though giving alias to respective columns shall be sufficient under the case:
Cursor c_no_debit :
c_no_debit_1: Based on table STTM_NO_DEBIT_customer a
c_no_debit_2: Based on table STTM_FIN_CYCLE b
Through parameterized cursor pass value of of cursor_1 into cursor_2.
Tables STTM_NO_DEBIT_CUSTOMER and STTM_FIN_CYCLE both have a column named FIN_CYCLE, so when the PL/SQL compiler tries to construct the record j_indx from c_no_debit, it gets something like this:
( fin_cycle number
, from_date date
, to_date date
, account_class varchar2(20)
, fin_cycle number
, ...
which is invalid because a record can't have two fields with the same name.
Change c_no_debit to specify only the columns you need, for example:
cursor c_no_debit is
select a.account_class
from sttm_no_debit_customer a
join sttm_fin_cycle b on b.fin_cycle = a.fin_cycle;
(and maybe other columns - I don't have your schema and I don't know what it needs to do)

Oracle query result as JSON

I'm working in Oracle 12.2.
I've got a complex query the results of which I would like to receive as a CLOB in JSON format. I've looked into json_object, but this means completely rewriting the query.
Is there a way to simply pass the ref cursor or result set and receive a JSON array with each row being a JSON object inside?
My query:
SELECT
*
FROM
(
SELECT
LABEL_USERS.*,
ROWNUM AS RANK ,
14 AS TOTAL
FROM
(
SELECT DISTINCT
SEC_VS_USER_T.USR_ID,
SEC_VS_USER_T.USR_FIRST_NAME,
SEC_VS_USER_T.USR_LAST_NAME,
SEC_USER_ROLE_PRIV_T.ROLE_ID,
SEC_ROLE_DEF_INFO_T.ROLE_NAME,
1 AS IS_LABEL_MANAGER,
LOWER(SEC_VS_USER_T.USR_FIRST_NAME ||' '||SEC_VS_USER_T.USR_LAST_NAME) AS
SEARCH_STRING
FROM
SEC_VS_USER_T,
SEC_USER_ROLE_PRIV_T,
SEC_ROLE_DEF_INFO_T
WHERE
SEC_VS_USER_T.USR_ID = SEC_USER_ROLE_PRIV_T.USR_ID
AND SEC_VS_USER_T.USR_SITE_GRP_ID IS NULL
ORDER BY
UPPER(USR_FIRST_NAME),
UPPER(USR_LAST_NAME)) LABEL_USERS) LABEL_USER_LIST
WHERE
LABEL_USER_LIST.RANK >= 0
AND LABEL_USER_LIST.RANK < 30
I couldn't find a procedure which I could use to generate the JSON, but I was able to use the new 12.2 functions to create the JSON I needed.
SELECT JSON_ARRAYAGG( --Used to aggregate all rows into single scalar value
JSON_OBJECT( --Creating an object for each row
'USR_ID' VALUE USR_ID,
'USR_FIRST_NAME' VALUE USR_FIRST_NAME,
'USR_LAST_NAME' VALUE USR_LAST_NAME,
'IS_LABEL_MANAGER' VALUE IS_LABEL_MANAGER,
'SEARCH_STRING' VALUE SEARCH_STRING,
'USR_ROLES' VALUE USR_ROLES
)returning CLOB) AS JSON --Need to cpecify CLOB, otherwise the result is limited by VARCHARC2(4000)
FROM
(
SELECT * FROM (
SELECT LABEL_USERS.*, ROWNUM AS RANK, 14 AS TOTAL from
(SELECT
SEC_VS_USER_T.USR_ID,
SEC_VS_USER_T.USR_FIRST_NAME,
SEC_VS_USER_T.USR_LAST_NAME,
1 AS IS_LABEL_MANAGER,
LOWER(SEC_VS_USER_T.USR_FIRST_NAME ||' '||SEC_VS_USER_T.USR_LAST_NAME) AS SEARCH_STRING,
(
SELECT --It is much easier to create the JSON here and simply use this column in the outer JSON_OBJECT select
JSON_ARRAYAGG(JSON_OBJECT('ROLE_ID' VALUE ROLE_ID,
'ROLE_NAME' VALUE ROLE_NAME)) AS USR_ROLES
FROM
(
SELECT DISTINCT
prv.ROLE_ID,
def.ROLE_NAME
FROM
SEC_user_ROLE_PRIV_T prv
JOIN
SEC_ROLE_DEF_INFO_T def
ON
prv.ROLE_ID = def.ROLE_ID
ORDER BY
ROLE_ID DESC)) AS USR_ROLES
FROM
SEC_VS_USER_T,
SEC_USER_ROLE_PRIV_T,
SEC_ROLE_DEF_INFO_T
WHERE
SEC_VS_USER_T.USR_ID = SEC_USER_ROLE_PRIV_T.USR_ID
AND SEC_USER_ROLE_PRIV_T.ROLE_PRIV_ID = SEC_ROLE_DEF_INFO_T.ROLE_ID
AND SEC_VS_USER_T.USR_SITE_GRP_ID IS NULL
ORDER BY UPPER(USR_FIRST_NAME),
UPPER(USR_LAST_NAME))LABEL_USERS)) LABEL_USER_LIST
WHERE LABEL_USER_LIST.RANK >= 0--:bv_Min_Rows
AND LABEL_USER_LIST.RANK < 30--:bv_Max_Rows

PL/SQL how to return a User-Defined Record from create or replace function

I'm trying to learn PL/SQL
and I do not seem to understand how I can create a function and let it return a Record
I am trying to do something like this:
create or replace FUNCTION getMovie(movieID number)
RETURN record IS titleAndYear record(title varchar(100), production_Year number);
BEGIN
if (titleAndYear is null) then
titleAndYear:= MovieTitleAndYear('',0);
end if;
select TITLE ,YEAR into titleAndYear.title ,titleAndYear.production_Year from movie where MOVIE_ID = movieID;
return titleAndYear;
END;
I know this is not working but I do not know why ?
EDIT 1:
I have also Tried this:
create or replace TYPE MovieTitleAndYear is OBJECT(title varchar(100), production_Year number);
/
create or replace FUNCTION getMovie(movieID number)
RETURN MovieTitleAndYear IS titleAndYear MovieTitleAndYear;
BEGIN
if (titleAndYear is null) then
titleAndYear:= MovieTitleAndYear('',0);
end if;
select TITLE ,YEAR into titleAndYear.title ,titleAndYear.production_Year from movie where MOVIE_ID = movieID;
return titleAndYear;
END;
But then the result when i run this statement:
select
GETMOVIE(
2540943
) from dual;
becomes this
GETMOVIE(2540943)
1 [DB_036.MOVIETITLEANDYEAR]
instead of two colums title and productionyear.
Your first example using a record won't work. For a start, a function cannot return a value of a type that is only declared inside the function. You can try moving the record type declaration to a package, but even if you then got the function to compile, running the query select getMovie(2540943) from dual would return an ORA-00902 invalid datatype error.
So I would recommend that you use a type instead.
If you are using a type, then to get the movie and year separately, you need to access the fields within the type individually, for example:
select getMovie(2540943).title, getMovie(2540943).production_year from dual
Alternatively, you can use a subquery if you want to avoid calling getMovie() twice:
select x.movie_info.title, x.movie_info.production_year from (select getMovie(2540943) as movie_info from dual) x;
Note that we need to use an alias for the subquery. The following will give an ORA-00904: "MOVIE_INFO"."PRODUCTION_YEAR": invalid identifier error:
select movie_info.title, movie_info.production_year from (select getMovie(2540943) as movie_info from dual);
The problem here is that Oracle is looking for a table movie_info in the query, but it can't find one. It doesn't realise that movie_info is actually a column. If we introduce the alias x, Oracle then realises that x.movie_info is a column and so x.movie_info.title is a field within a type in the column.
Try this approach. I think your answer lies within this snippet. Let
me know if this helps.
--Create object type
CREATE OR REPLACE type av_obj_test
IS
object
(
col1 VARCHAR2(100),
col2 VARCHAR2(100) );
--C reate table type
CREATE OR REPLACE type av_ntt_test
IS
TABLE OF av_obj_test;
--Createfunction
CREATE OR REPLACE FUNCTION AV_RECORD RETURN
AV_NTT_TEST
AS
av_record av_ntt_test;
BEGIN
NULL;
SELECT av_obj_test(LEVEL,'av'||LEVEL) BULK COLLECT INTO av_record FROM DUAL
CONNECT BY LEVEL < 10;
RETURN av_record;
END;
--Calling function
SELECT * FROM TABLE(AV_RECORD);
--------------------------------OUTPUT-------------------------------------
COL1 COL2
1 av1
2 av2
3 av3
4 av4
5 av5
6 av6
7 av7
8 av8
9 av9
-------------------------------OUTPUT----------------------------------------

How to swap values between before and after `=` using Oracle?

I have declared a value in parameter #Data as ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1
I need to get a result as ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M.
Which means I need to swap between the values before and after =
I have the SQL Server Query for achieving this but I need Oracle query.
Declare #Data varchar(100)='ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1';
WITH
myCTE1 AS
(
SELECT CAST('<root><r>' + REPLACE(#Data,',','</r><r>') + '</r></root>' AS XML) AS parts1
)
,myCTE2 AS
(
SELECT CAST('<root><r>' + REPLACE(p1.x.value('.','varchar(max)'),'=','</r><r>') + '</r></root>' AS XML) as parts2
FROM myCTE1
CROSS APPLY parts1.nodes('/root/r') AS p1(x)
)
SELECT STUFF
(
(
SELECT ',' + parts2.value('/root[1]/r[2]','varchar(max)') + '=' + parts2.value('/root[1]/r[1]','varchar(max)')
FROM myCTE2
FOR XML PATH(''),TYPE
).value('.','varchar(max)'),1,1,'');
Expected Output if I execute the query ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M. Can anyone give an idea to do this one?
Sounds like a job for REGEXP_REPLACE:
WITH datatab as (select 'ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1' info from dual)
select info,
regexp_replace(info, '([^=]+)=([^=,]+),([^=]+)=([^=,]+)', '\2=\1,\4=\3') new_info
from datatab;
INFO NEW_INFO
--------------------------------------------- ---------------------------------------------
ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1 ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M
(as a complete aside, that's the first time I've ever written a regular expression and had it work first time. Apparently, I have gone over to the dark side... *{;-) )
ETA: If you need this in a procedure/function, you don't need to bother selecting the regular expression, you can do it in PL/SQL directly.
Here's an example of a function that returns the swapped over result:
create or replace function swap_places (p_data in varchar2)
return varchar2
is
begin
return regexp_replace(p_data, '([^=]+)=([^=,]+),([^=]+)=([^=,]+)', '\2=\1,\4=\3');
end swap_places;
/
-- example of calling the function to check the result
select swap_places('ACCOUNT_NO|none|M=ACCOUNT_NO,ADD1|none|M=ADD1') col1 from dual;
COL1
-------------------------------------------------
ACCOUNT_NO=ACCOUNT_NO|none|M,ADD1=ADD1|none|M

CAST(NULL AS DATE)

I am trying to create a view in oracle which is already defined in DB2,in the DB2 view all date column having cast function , Below i have created a view by taking one date column from the view. Note:COBDATE column having number datatype as for the design. i am getting ORA-00936:missing expression error.
Create View MYVIEWV1 as (Select
A.COBDATE ,
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
(case when (COBDATE = 0)
then CAST(NULL AS DATE)
else
date(SUBSTR(COBDATE,1,4) ||
'-'||SUBSTR(COBDATE,5,2) ||
'-'||SUBSTR(COBDATE,7,2))
end ) as COBDATE2
from MYTBLT1 A;
please suggest how i can convert this to oracle syntax?
The cast isn't the issue, though it isn't really needed. The 'missing expression' error is reported against line 7 column 4, which is the call to the date() function - which doesn't exist in Oracle. It's expecting a date literal to follow the keyword date, not parentheses or function arguments.
You can use the to_date() function instead, with a format model matching the way you're constructing the first argument. You said cobdate is a number but you're treating it as a string so I'll continue to do that implicitly...
create table mytblt1 (cobdate number);
insert into mytblt1 values (20141225);
insert into mytblt1 values (0);
create view myviewv1 as
select cobdate,
case when cobdate = 0
then null -- or with unnecessary cast: cast(null as date)
else
to_date(substr(cobdate,1,4)
||'-'||substr(cobdate,5,2)
||'-'||substr(cobdate,7,2),
'YYYY-MM-DD')
end as cobdate2
from mytblt1;
select * from myviewv1;
COBDATE COBDATE2
---------- -------------------
20141225 2014-12-25 00:00:00
0
desc myviewv1
Name Null Type
-------- ---- ------
COBDATE NUMBER
COBDATE2 DATE
With a cut-down date format model - so you don't have to worry about the - separators - you can simplify it further:
create view myviewv1 as
select cobdate,
case when cobdate = 0
then null -- cast(null as date)
else
to_date(cobdate, 'YYYYMMDD')
end as cobdate2
from mytblt1;
Or you can move the case inside the function call:
create view myviewv1 as
select cobdate,
to_date(case when cobdate = 0 then null else cobdate end, 'YYYYMMDD') as cobdate2
from mytblt1;
Nulls don't need casting. Just use NULL on its own.
case when COBDATE = 0 then NULL
else <some date> end as COBDATE2
Don't need all those brackets either.

Resources