This issue is linked to another issue which I posted yesterday
oracle procedure to list table names and corresponding count
I am getting an XML error while querying the below query:
CREATE OR REPLACE VIEW V_CHECK_RECORDS_BUL AS
SELECT TABLE_NAME,
case
when S.TABLE_NAME_BUL in ('GD_MSTR_FLAT_CLAIM_POL','GD_MSTR_ERROR') then
TO_NUMBER(
EXTRACTVALUE( XMLTYPE(
DBMS_XMLGEN.GETXML('select count(*) c from ' || U.TABLE_NAME ||'where oe_name="BUL"')
), '/ROWSET/ROW/C'))
else
TO_NUMBER(
EXTRACTVALUE( XMLTYPE(
DBMS_XMLGEN.GETXML('select count(*) c from ' || U.TABLE_NAME)
), '/ROWSET/ROW/C'))
end as
NUM_ROWS
FROM USER_TABLES U JOIN GD_TABLE_ORDER S ON S.TABLE_NAME_BUL = U.TABLE_NAME order by S.order_id;
ERROR:
ORA-19202: Error occurred in XML processing
ORA-00933: SQL command not properly ended
ORA-06512: at "SYS.DBMS_XMLGEN", line 176
ORA-06512: at line 1
19202. 00000 - "Error occurred in XML processing%s"
*Cause: An error occurred when processing the XML function
*Action: Check the given error message and fix the appropriate problem
This error is occured when I use case statement to add a condition, otherwise it is working fine.
The view will be created, but during the selction of the view, this error is occuring.
The error is because you are missing a space in the inner query; but your quotes are also wrong; this:
U.TABLE_NAME ||'where oe_name="BUL"')
should be
U.TABLE_NAME ||' where oe_name=''BUL''')
----------------^
If you have a string literal that contains a single quote then you need to escape that with another single quote; two single quotes ('') are not the same as one double quote (").
Also, extractvalue is deprecated. You can do the same thing with xmlquery instead:
TO_NUMBER(
XMLQUERY(
'/ROWSET/ROW/C/text()'
PASSING XMLTYPE(
DBMS_XMLGEN.GETXML(
'select count(*) c from "' || U.TABLE_NAME || '" where oe_name=''BUL'''
)
)
RETURNING CONTENT
)
)
I tend to include double quotes around object names for this sort of thing just in case it ever has to deal with mixed-cased (quoted) identifiers.
So put together than becomes:
create or replace view v_check_records_bul as
select table_name,
case
when s.table_name_bul in ('GD_MSTR_FLAT_CLAIM_POL','GD_MSTR_ERROR') then
to_number(
xmlquery(
'/ROWSET/ROW/C/text()'
passing xmltype(
dbms_xmlgen.getxml(
'select count(*) c from "' || u.table_name || '" where oe_name=''BUL'''
)
)
returning content
)
)
else
to_number(
xmlquery(
'/ROWSET/ROW/C/text()'
passing xmltype(
dbms_xmlgen.getxml(
'select count(*) c from "' || u.table_name || '"'
)
)
returning content
)
)
end as num_rows
from user_tables u
join gd_table_order s on s.table_name_bul = u.table_name;
I've left out the order by clause as that really belongs as part of queries against the view, not as part of the view definition - though you would have to include it as a column in the view if you wanted to be able to use it for ordering later.
db<>fiddle demo
Related
Impossible to get back the Query result for PL/SQL scripts. It was working fine earlier in the afternoon but something might have gone wrong and now results have vanished from screen. What should I do?
Thank you!
The dummy query is just an example even if I should also get DUMMY as column header and X as a unique record in a "Query result" tab. In fact my script is such as below and works but gives no viewable data because the "Query Result" has gone. Before to reinstall Oracle SQL Dev I am wondering if on trick exists to fix this point for which I have found no valuable fix so far.
declare
v_tab varchar( 30 ):= 'MD_ALL_DETAIL_';
v_date1 varchar( 30 ):= '0827D';
v_date2 varchar( 30 ):= '0830';
v_tab1 varchar( 30 );
v_tab2 varchar( 30 );
v_sql varchar( 2000 );
begin
v_tab1 := v_tab || v_date1;
v_tab2 := v_tab || v_date2;
v_sql := '
with tab as(
select ' || v_date1 || ' flag, md1.*
from ' || v_tab1 || ' md1
union all
select ' || v_date2 || ' flag, md2.*
from ' || v_tab2 || ' md2
), tab2 as (
select *
from(
select tab.flag, tab.cat_mk_code, tab.cat_mk_iden, tab.b_entity_code, tab.b_entity_iden, tab.cat_be_code, tab.cat_be_iden, tab.bucket_code, tab.
bucket_iden, tab.m_entity_code, tab.application_code, tab.indicator_code, tab.indicator_iden, tab.measure_value, tab.user_code, tab.lock_flag,
tab.alter_date, tab.table_name
from tab
)pivot(
sum( measure_value )
for flag
in( ' || v_date1 || ' as MEASURE_0, ' || v_date2 || ' as MEASURE_1 )
) )
select * from tab2 where MEASURE_0<>MEASURE_1';
execute immediate v_sql;
end;'
This code (at the screenshot) doesn't do anything, so message you got ("PL/SQL procedure successfully completed") is all you can expect.
Run e.g.
select * from dual;
and SQL Developer will automatically open the missing "Query Result" tab.
If you want to "display" the result of a dynamic SQL, you'll have to select it into something. A simple example:
See? I'm selecting from DUAL (as your initial query does) INTO a local variable. The result is displayed within the Script output tab.
Your "new" code uses a more complex query, possibly returning more than a single column/row so you can't select into a scalar variable. I presume you'll have to create a TYPE, declare a local variable (a collection, probably) and then do something with the result.
https://docs.oracle.com/cd/E11882_01/appdev.112/e25519/executeimmediate_statement.htm#LNPLS01317
If dynamic_sql_statement is a SELECT statement, and you omit both into_clause and bulk_collect_into_clause, then execute_immediate_statement never executes.
For example, this statement never increments the sequence:
EXECUTE IMMEDIATE 'SELECT S.NEXTVAL FROM DUAL'
declare #sql varchar(max)
declare #pimkeys varchar(max)
set #pimkeys = '69966,69965' -- input comma delimited values inside single quotation marks
SET #SQL =
'with basedata as(
SELECT
ec.Pim_key,pm.Parent_PIM_Key,pm.Item_Type,pm.Company_name,pm.Part_number,pm.Product_Desc,pm.Novation_Description
,ec.Name ,ec.Value
FROM edw..Products_PPS_Attributes AS ec
join edw..DM_Product_Master as pm on ec.Pim_Key = pm.PIM_Key
where ec.pim_key in ('+#pimkeys+'))
Select * from basedata
PIVOT ( MAX(Value) FOR Name IN( Sync_Code,Product_Line_Brand ) ) AS p'
execute (#SQL)
SQL Developer is the platform Oracle provides for doing SQL Server migrations. One of the features is our scratch editor.
You can paste in T-SQL, and it will spit out PL/SQL.
Tools > Migration > Scratch Editor
It's not guaranteed to have a 100% successful translation, just like any other translator, but should give you a nice start.
Here's what we end up with, without any testing.
DECLARE
v_pimkeys VARCHAR2(4000);
v_SQL varchar2(4000); -- i had to manually add this after the translation
BEGIN
v_pimkeys := '69966,69965' ;-- input comma delimited values inside single quotation marks
v_SQL := 'with basedata as(
SELECT
ec.Pim_key,pm.Parent_PIM_Key,pm.Item_Type,pm.Company_name,pm.Part_number,pm.Product_Desc,pm.Novation_Description
,ec.Name ,ec.Value
FROM edw..Products_PPS_Attributes AS ec
join edw..DM_Product_Master as pm on ec.Pim_Key = pm.PIM_Key
where ec.pim_key in (' || v_pimkeys || '))
Select * from basedata
PIVOT ( MAX(Value) FOR Name IN( Sync_Code,Product_Line_Brand ) ) AS p' ;
EXECUTE IMMEDIATE v_SQL;
END;
Here is my code. Please forgive me for not putting variables in declaration section as the editor was giving me tough time in formatting it before I could submit my issue.
I want the result variable (v_Var) to have value printed as
v_ID = :NEW.ID;
v_NAME = :NEW.NAME;
v_ENTITY_ID = :NEW.ENTITY_ID;
BUT, it is getting printed as
v_ID = :NEW.ID;
v_ID = :NEW.ID;
v_NAME = :NEW.NAME;
v_ENTITY_ID = :NEW.ENTITY_ID;
Since the table TEMP_TRG_CONSTRNT has 2 rows, it is working for v_ID also for two times.
I know the issue is with external FOR loop but I am not sure how to handle it.
DECLARE
CURSOR c1 IS
SELECT NAME, OCCUR_COUNT FROM IFMS_SYSTEMCONFIGURATION.TEMP_TRG_CONSTRNT;
BEGIN
v_TableName := 'MyTable';
EXECUTE IMMEDIATE 'TRUNCATE TABLE IFMS_SYSTEMCONFIGURATION.TEMP_TRG_CONSTRNT';
INSERT INTO IFMS_SYSTEMCONFIGURATION.TEMP_TRG_CONSTRNT (NAME, OCCUR_COUNT)
SELECT A.FKN, COUNT(A.FKN) AS OCCUR_COUNT FROM
(
SELECT A.CONSTRAINT_NAME AS FKN FROM ALL_CONSTRAINTS A
INNER JOIN ALL_CONS_COLUMNS B
ON A.CONSTRAINT_NAME = B.CONSTRAINT_NAME
WHERE A.CONSTRAINT_TYPE IN ('P', 'U') AND A.TABLE_NAME = 'MyTable'
)A
GROUP BY A.FKN;
--FOR CONSTR_NAME IN (SELECT NAME FROM IFMS_SYSTEMCONFIGURATION.TEMP_TRG_CONSTRNT)
FOR CONSTR_NAME IN c1
LOOP
--SELECT NAME, OCCUR_COUNT INTO v_Constr_Name, v_Index_Count FROM TEMP_TRG_CONSTRNT WHERE NAME = CONSTR_NAME.NAME;
FOR COL_NAME IN (SELECT COLUMN_NAME FROM ALL_CONS_COLUMNS WHERE CONSTRAINT_NAME = CONSTR_NAME.NAME)
LOOP
v_Var := v_Var || 'v_' || COL_NAME.COLUMN_NAME || ' = :NEW.' || COL_NAME.COLUMN_NAME || ';' || CHR(13);
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_Var);
END LOOP;
END;
There could be a couple of things causing your results:
There are tables in different schemas with the same name (you're missing a join on owner between the two tables)
There could be more than one constraint on the same table.
In addition to those, you've also managed to recreate a Nested Loop join, by doing the nested looping. In general, this is a bad idea - what if a Hash Join was more performant? You will have effectively hobbled Oracle by using your nested cursor for loops. At the very least, you could join your two cursors in a single sql statement before looping through it.
However, it looks like you're trying to generate a list of variables without having to type them. You can do this in a single SQL statement - no need for PL/SQL, like so:
SELECT DISTINCT con.constraint_name,
con.owner,
con.table_name,
col.column_name,
'v_'||col.column_name||' := :NEW.'||col.column_name||';'
FROM all_constraints con
inner JOIN all_cons_columns col ON con.constraint_name = col.constraint_name
AND con.owner = col.owner
AND con.constraint_type IN ('P', 'U')
--AND con.owner = 'SOME_OWNER' -- uncomment out and put in the correct owner name, esp if your table exists in more than one owner.
AND con.table_name = 'YOUR_TABLE'
ORDER BY con.owner, con.table_name;
Note that I've included extra columns, so that you can work out why you're getting the results you're getting, just in case that doesn't match what you're expecting to see. I included the DISTINCT keyword to take care of the case where you have multiple constraints returned for a single owner.table.
If you want to generate a list of variables for multiple tables at once, you might want to use the aggregate listagg function on the above query (meaning you could remove the DISTINCT) with a delimiter of CHR(10).
I have a function:
At line execute immediate 'select ' || schemaname || '.' || value1 || '_seq.nextval from dual' into cnpParmId;
Am getting error as SQL Error: ORA-00904: "CNPPARMID": invalid identifier.
I tried to put the cnpParmId inside the quotes, tried into cnpParmId from dual, in all possible ways. But its not working.
Please give me some ideas to solve this issue.
Thanks!!
Your function compiles successfully, and you get the error at runtime:
select test(user, 'T42') from dual;
SQL Error: ORA-00904: "CNPPARMID": invalid identifier
ORA-06512: at "MYSCHEMA.TEST", line 23
You said the error was on the first execute immediate, but that is line 21 not line 23, and if it was that cnpParmId reference it was complaining about then it would cause a compilation error - the function would be created but with errors/warnings, and it wouldn't be possible to call it.
So it's the second execute immediate, at line 23, which is erroring at runtime (reformatted slightly):
execute immediate
'select ''T'' from dual where cnpParmId not in ' ||
'(select value1 from ' || schemaname || '.' || tablename || ')'
into good;
As GolezTrol said, the dynamic statement is executed in a SQL context that has no visibility of any of your PL/SQL variables. It's the same as running the generated statement:
select 'T' from dual where cnpParmId not in (select value1 from myschema.t42);
... directly in SQL*Plus or SQL Developer, which also gets:
SQL Error: ORA-00904: "CNPPARMID": invalid identifier
00904. 00000 - "%s: invalid identifier"
As a variation of GolezTrol's concatenation, you could use a bind variable to prevent hard-parsing each time round your loop, but you also need to provide your primary key column name as value1 also won't be recognised; and that has to be concatenated in:
execute immediate
'select ''T'' from dual where :cnpParmId not in ' ||
'(select ' || value1 || ' from ' || schemaname || '.' || tablename || ')'
into good using cnpParmId;
which compiles and runs.
You could also use not exists rather than not in, which might perform better since you're looking for the (indexed) primary key:
execute immediate
'select ''T'' from dual where not exists (select null from '
|| schemaname || '.' || tablename || ' where ' || value1 || ' = :cnpParmId)'
into good using cnpParmId;
You can also move the query that finds value1 outside the loop; there's no benefit to calling that repeatedly.
It looks like you're doing this because you have primary key values that weren't generated from the sequence. If you're still adding new records like that - e.g. via a trigger that only uses the sequence if the passed key column is null - then you need a hack like this or an insert loop that catches the ORA-01001. But this approach still has a race condition - another session can simultaneously do a manual insert with the same value your function finds, and one of the sessions will get an error.
It would usually be better to only use the sequence; if you are now doing that, or can change to do that, then a one-off adjustment of all your sequences to be higher than the current maximum key value would be simpler.
Using execute immediate, you execute the statement outside of the scope of the function, so it can't use the PLSQL variable. I'd solve this by executing it as a normal query and use SELECT INTO or a cursor to fetch the query result.
But it should also work if you simply substitute the value into the query string yourself, like this:
Change
'select ''T'' from dual where cnpParmId not in ' ||
into
'select ''T'' from dual where ' || cnpParmId || ' not in ' ||
Here is the package created by passing 3 input parameters to function
CREATE OR replace PACKAGE "PKG_CAMPAIGN_EMAIL_QTY"
AS
FUNCTION Getcampaignoutgoingemailqty(
tablename IN VARCHAR2,
ActivatedDate IN DATE,
CompletedDate IN DATE)
RETURN NUMBER;
END pkg_campaign_email_qty;
/
Here is the query to get the count
SELECT
(
pkg_campaign_email_qty.Getcampaignoutgoingemailqty(
9142632263013677974,
To_date('20/10/2015', 'DD/MM/YYYY'),
To_date('30/11/2015', 'DD/MM/YYYY')
)
) AS
email
FROM dual;
Getting ORA-00933: SQL command not properly ended in oracle
here is the package body
CREATE OR REPLACE PACKAGE BODY "PKG_CAMPAIGN_EMAIL_QTY" as
FUNCTION getCampaignOutgoingEmailQty(tableName IN VARCHAR2 ,ActivatedDate DATE,CompletedDate DATE) RETURN NUMBER IS
OutgoingEmailQuantity NUMBER;
begin
EXECUTE IMMEDIATE 'select NVL(COUNT(1),0) from campaign_'||tableName||'
join flat_interactions out_email on campaign_'||tableName||'.fullname=out_email.o_parent_id and out_email.N9135700037713613964=9135706250013621563 and out_email.D9135699928113613119 between TO_DATE(''ActivatedDate'',''MM/dd/YYYY'') and TO_DATE(''CompletedDate'',''MM/dd/YYYY'')' INTO OutgoingEmailQuantity;
RETURN OutgoingEmailQuantity ;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
end getCampaignOutgoingEmailQty;
end PKG_CAMPAIGN_EMAIL_QTY;
/
I don't know if this is the cause of the error you're seeing, but there's an issue with the function code - if you look inside the execute immediate, you've got
'<snip> and out_email.D9135699928113613119 between TO_DATE(''ActivatedDate'',''MM/dd/YYYY'') and TO_DATE(''CompletedDate'',''MM/dd/YYYY'')'
This means that, passing those parameters in, you'll end up trying to execute a sql statement of:
select NVL(COUNT(1),0)
from campaign_9142632263013677974
join flat_interactions out_email on campaign_9142632263013677974.fullname = out_email.o_parent_id
and out_email.N9135700037713613964 = 9135706250013621563
and out_email.D9135699928113613119 between TO_DATE('ActivatedDate','MM/dd/YYYY')
and TO_DATE('CompletedDate','MM/dd/YYYY');
So, you're trying to convert the strings "ActivatedDate" and "CompletedDate" into dates when clearly they're not dates.
Rather than do that, I would use bind variables, something like:
create or replace package body pkg_campaign_email_qty
as
function getcampaignoutgoingemailqty(tablename in varchar2 ,activateddate date,completeddate date)
return number
is
outgoingemailquantity number;
begin
execute immediate 'select NVL(COUNT(1),0)'||chr(10)||
'from campaign_'||tablename||chr(10)||
' join flat_interactions out_email on campaign_'||tablename||'.fullname=out_email.o_parent_id'||chr(10)||
' and out_email.N9135700037713613964=9135706250013621563'||chr(10)||
' and out_email.D9135699928113613119 between :ActivatedDate and :CompletedDate' into outgoingemailquantity using activateddate, completeddate;
return outgoingemailquantity;
exception
when others then
return 0;
end getcampaignoutgoingemailqty;
end pkg_campaign_email_qty;
/
N.B. untested, since you didn't provide any table definitions.
As an aside, with your calling query, the outer brackets are unnecessary and I would remove them, so your query would become:
SELECT pkg_campaign_email_qty.Getcampaignoutgoingemailqty(9142632263013677974,
To_date('20/10/2015', 'DD/MM/YYYY'),
To_date('30/11/2015', 'DD/MM/YYYY')) email
FROM dual;
You are using wrong the dates in the dynamic sql.
I will use binds for that:
EXECUTE IMMEDIATE
'select NVL(COUNT(1),0)
from campaign_'||tableName||'
join flat_interactions out_email on
campaign_'||tableName||'.fullname=out_email.o_parent_id and
out_email.N9135700037713613964=9135706250013621563 and
out_email.D9135699928113613119 between :ActivatedDate and :CompletedDate'
INTO OutgoingEmailQuantity
USING ActivatedDate, CompletedDate;
What you want to do is harder to follow:
EXECUTE IMMEDIATE
'select NVL(COUNT(1),0)
from campaign_'||tableName||'
join flat_interactions out_email on
campaign_'||tableName||'.fullname=out_email.o_parent_id and
out_email.N9135700037713613964=9135706250013621563 and
out_email.D9135699928113613119 between
TO_DATE('||to_char(ActivatedDate,'MM/dd/YYYY')||',''MM/dd/YYYY'')
and
TO_DATE('||to_char(CompletedDate,'MM/dd/YYYY')||',''MM/dd/YYYY'')'
INTO OutgoingEmailQuantity;
SELECT
(
pkg_campaign_email_qty.Getcampaignoutgoingemailqty(9142632263013677974, To_date('20/10/2015', 'DD/MM/YYYY'), To_date('30/11/2015', 'DD/MM/YYYY')) ) AS
email
FROM dual
); <- missing