Inner join condition is disregarded - looks like an Oracle bug - sql

How is this possible?: If I replace message_format_name with the literal 'MT202', the query works as expected. Otherwise, it returns 2 rows instead of the expected 1--same as if that condition was commented out.
It seems the parameter value passed in to the stored proc doesn't equal the value in ms.message_format_name for some strange reason.
Let me know if you need more details.
Thanks!
set define off;
create or replace PROCEDURE insert_mapping(
interface_name IN VARCHAR2,
message_format_name IN VARCHAR2,
determined_field_type IN VARCHAR2,
source_field IN VARCHAR2,
mapping_key IN VARCHAR2,
mapping_key_value1 IN VARCHAR2,
mapping_key_value2 IN VARCHAR2 DEFAULT NULL,
return_val OUT sys_refcursor)
AS
BEGIN
dbms_output.put_line('interface_name = ' || interface_name);
dbms_output.put_line('message_format_name = ' || message_format_name);
dbms_output.put_line('determined_field_type = ' || determined_field_type);
dbms_output.put_line('source_field = ' || source_field);
/* INSERT
INTO payments.multi_value_lookup_mapping
(
pk_multi_value_lkp_mapping,
fk_pk_multi_value_lookup_confg,
mapping_key,
mapping_key_value1,
mapping_key_value2,
created_by,
created_dt,
update_by,
update_dt
)*/
OPEN return_val FOR
SELECT payments.seq_multi_value_lookup_map.nextval,
mvlc.pk_multi_value_lookup_config,
mapping_key,
mapping_key_value1,
mapping_key_value2,
50000,
SYSDATE,
50000,
SYSDATE
FROM payments.multi_value_lookup_config mvlc, payments.message_source ms
WHERE (ms.pk_message_source = mvlc.fk_pk_message_source
AND ms.interface_name = interface_name
AND ms.message_format_name = message_format_name /*'MT202'*/
AND mvlc.mapping_column_name = source_field
AND mvlc.lookup_category_type = determined_field_type
);
END insert_mapping;

This is not an Oracle bug. First, you should never use commas in the FROM clause. You should always use explicit JOIN syntax.
But that is not your specific problem. Variable resolution and scoping is.
When you have a reference such as interface_name in a query, then Oracle looks first for columns with that name. It never sees the variables. Name the variables something distinguishing, so you end up with code that is more like this:
. . .
FROM payments.multi_value_lookup_config mvlc JOIN
payments.message_source ms
ON ms.pk_message_source = mvlc.fk_pk_message_source
WHERE ms.interface_name = v_interface_name
ms.message_format_name = v_message_format_name /*'MT202'*/
mvlc.mapping_column_name = v_source_field
mvlc.lookup_category_type = v_determined_field_type

Related

PL/SQL and SQL Developer different results from each other

I am executing a query in PL / SQL in version 7 and version 14, with a function created by me, and both bring me some results, the rest bring 0.
However, when executing the same query in Oracle SQL Developer, the query brings all the results correctly.
I executed the procedure through PL / SQL and Oracle SQL Developer as well, but then none brought me the right result, all the lines were left as "0".
I can't find the problem at all, even on Google.
Basically, the function multiplies the number of rows by columns that start with "ID_", as shown below.
Function:
CREATE OR REPLACE FUNCTION DS_FUNCESP.FNBIGB_CheckDataCells
(pOwn IN VARCHAR2,
pTab IN VARCHAR2)
RETURN NUMBER
IS
v_Qtd NUMBER;
v_str VARCHAR2(2000);
BEGIN
v_Qtd := 1;
v_str := ' SELECT
SUM((SELECT COUNT(1) AS QTY_ROWS FROM ' || pOwn || '.' || pTab || ' d WHERE d.LINORIGEM <> ''CARGA MANUAL'')) AS QTY_DATA
FROM DW_FUNCESP.D_BI_COLUMNS a
LEFT JOIN
DW_FUNCESP.D_BI_TABLES b
ON a.ID_TABLE = b.ID_TABLE
AND a.ID_OWNER = b.ID_OWNER
LEFT JOIN DW_FUNCESP.D_BI_OWNERS c
ON a.ID_OWNER = c.ID_OWNER
WHERE b.NM_TABLE = ''' || pTab || '''
AND a.IN_PRIMARYKEY = ''NAO''
AND SUBSTR(a.NM_COLUMN,1,3) = ''ID_'' ';
DBMS_OUTPUT.put_line(v_str);
EXECUTE IMMEDIATE v_str into v_Qtd ;
return (v_Qtd);
EXCEPTION WHEN OTHERS THEN
RETURN 0;
END FNBIGB_CheckDataCells;
Select statement:
SELECT
c.NM_OWNER ,
b.NM_TABLE ,
DS_FUNCESP.FNBIGB_CHECKDATACELLS(c.NM_OWNER, b.NM_TABLE) AS QTY_DATA
FROM DW_FUNCESP.D_BI_TABLES b
LEFT JOIN DW_FUNCESP.D_BI_OWNERS c
ON b.ID_OWNER = c.ID_OWNER;
Results from PL/SQL:
Results from Oracle SQL Developer:
Clearly we can see the difference from any row, the right one is the Oracle SQL Developer. So I'd like to know what is the problem, how to fix, because the procedure is adding "0" to all the rows, no matter where I run.
Reading those examples from WHEN OTHERS - A Bug, thanks to #Lalit Kumar B for that, I changed:
EXCEPTION WHEN OTHERS THEN
RETURN 0;
To:
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('SQLCODE: '||SQLCODE);
DBMS_OUTPUT.PUT_LINE('Message: '||SQLERRM);
RAISE;
To find out the problem, and thanks for that I found that it was trying to count from a table where it doesn't exist anymore.
So I using an error handling as below, from #Jeffrey Kemp
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
Also, thanks for #Belayer, my code was the problem, agreed on that. Also, executing on both softwares, made me even more confused. I'll read also that documentation for sure.

Passing char value to function

I have the following function:
CREATE OR REPLACE FUNCTION BANINST1."F_COC_AUTO_AWARD_FILTER" (pidm number) return number
as
return_field number;
cursor get_pidm is
select distinct SHRDGMR.SHRDGMR_PIDM
from SATURN.SHRDGMR SHRDGMR,
SATURN.SORLCUR SORLCUR,
SATURN.SORLFOS SORLFOS,
DWSCHEMA.DAP_AUDIT_DTL#LINKDWTEST
where SORLCUR.SORLCUR_PIDM = SHRDGMR.SHRDGMR_PIDM
and SORLFOS.SORLFOS_PIDM = SORLCUR.SORLCUR_PIDM
and SORLCUR.SORLCUR_LEVL_CODE = SHRDGMR.SHRDGMR_LEVL_CODE
and SORLCUR.SORLCUR_DEGC_CODE = SHRDGMR.SHRDGMR_DEGC_CODE
and SORLFOS.SORLFOS_TERM_CODE = SORLCUR.SORLCUR_TERM_CODE
and SHRDGMR.SHRDGMR_PIDM = pidm
and SHRDGMR.SHRDGMR_DEGS_CODE = 'AW'
and SORLCUR.SORLCUR_PROGRAM in ('STCC', 'CC')
and DWSCHEMA.DAP_AUDIT_DTL.DAP_DEGREE in ('CPCC-CDS', 'CC1-CDS', 'CC2-CDS')
and SORLFOS.SORLFOS_MAJR_CODE <> DWSCHEMA.DAP_AUDIT_DTL.DAP_AUD_VALUE2
and trim(DWSCHEMA.DAP_AUDIT_DTL.DAP_STU_ID) = (select spriden_id from spriden where spriden_pidm = pidm and spriden_change_ind is null);
begin
open get_pidm;
fetch get_pidm into return_field;
close get_pidm;
return return_field;
end;
/
This is the call to the function from a where clause:
baninst1.f_coc_auto_award_filter#test(RAD_PRIMARY_MST.RAD_USER_DEF1) is not null
The function accepts a number data type parameter.
The passed column RAD_PRIMARY_MST.RAD_USER_DEF1 is a char(12) data type that has a value such as 293858.
The following error is returned when calling the function: ORA-01722: invalid number
I have tried to pass a number value to the function in several different ways:
baninst1.f_coc_auto_award_filter#test(to_number(trim(RAD_PRIMARY_MST.RAD_USER_DEF1))) is not null
baninst1.f_coc_auto_award_filter#test(cast(RAD_PRIMARY_MST.RAD_USER_DEF1 as number(8))) is not null
All attempts return the same error ORA-01722: invalid number
If I hard-code the D_PRIMARY_MST.RAD_USER_DEF1 value to a number, the function call works.
baninst1.f_coc_auto_award_filter#test(293858) is not null
How do I pass the char value from RAD_PRIMARY_MST.RAD_USER_DEF1 to the function?
This might be the keyword:
such as 293858
Are you sure that there aren't any non-numeric values in that column? With that datatype (char), there's always doubt. If you are storing numbers in there, why isn't it number?
This query should return invalid values which cause your code to break.
select * from d_primary_mst where not regexp_like(rad_user_def1, '^\d+$')
Also, if you are passing a string, why do you force the function to accept a number? Why don't you
CREATE OR REPLACE FUNCTION BANINST1.F_COC_AUTO_AWARD_FILTER
(pidm RAD_PRIMARY_MST.RAD_USER_DEF1%type)
return SHRDGMR.SHRDGMR_PIDM%type
as
return_field SHRDGMR.SHRDGMR_PIDM%type;
cursor get_pidm is
select distinct SHRDGMR.SHRDGMR_PIDM ...
By the way, get rid of double quotes when creating Oracle objects; they only cause problems.

Oracle : String Concatenation is too long

I have below SQL as a part of a view. In one of the schema I am getting "String Concatenation is too long" error and not able to execute the view.
Hence I tried the TO_CLOB() and now VIEW is not throwing ERROR, but it not returning the result as well it keep on running..
Please suggest....
Sql:
SELECT Iav.Item_Id Attr_Item_Id,
LISTAGG(La.Attribute_Name
||'|~|'
|| Lav.Attribute_Value
||' '
|| Lau.Attribute_Uom, '}~}') WITHIN GROUP (
ORDER BY ICA.DISP_SEQ,LA.ATTRIBUTE_NAME) AS ATTR
FROM Item_Attribute_Values Iav,
Loc_Attribute_Values Lav,
Loc_Attribute_Uoms Lau,
Loc_Attributes La,
(SELECT *
FROM Item_Classification Ic,
CATEGORY_ATTRIBUTES CA
WHERE IC.DEFAULT_CATEGORY='Y'
AND IC.TAXONOMY_TREE_ID =CA.TAXONOMY_TREE_ID
) ICA
WHERE IAV.ITEM_ID =ICA.ITEM_ID(+)
AND IAV.ATTRIBUTE_ID =ICA.ATTRIBUTE_ID(+)
AND Iav.Loc_Attribute_Id =La.Loc_Attribute_Id
AND La.Locale_Id =1
AND Iav.Loc_Attribute_Uom_Id =Lau.Loc_Attribute_Uom_Id(+)
AND Iav.Loc_Attribute_Value_Id=Lav.Loc_Attribute_Value_Id
GROUP BY Iav.Item_Id;
Error:
ORA-01489: result of string concatenation is too long
01489. 00000 - "result of string concatenation is too long"
*Cause: String concatenation result is more than the maximum size.
*Action: Make sure that the result is less than the maximum size.
You can use the COLLECT() function to aggregate the strings into a collection and then use a User-Defined function to concatenate the strings:
Oracle Setup:
CREATE TYPE stringlist IS TABLE OF VARCHAR2(4000);
/
CREATE FUNCTION concat_List(
strings IN stringlist,
delim IN VARCHAR2 DEFAULT ','
) RETURN CLOB DETERMINISTIC
IS
value CLOB;
i PLS_INTEGER;
BEGIN
IF strings IS NULL THEN
RETURN NULL;
END IF;
value := EMPTY_CLOB();
IF strings IS NOT EMPTY THEN
i := strings.FIRST;
LOOP
IF i > strings.FIRST AND delim IS NOT NULL THEN
value := value || delim;
END IF;
value := value || strings(i);
EXIT WHEN i = strings.LAST;
i := strings.NEXT(i);
END LOOP;
END IF;
RETURN value;
END;
/
Query:
SELECT Iav.Item_Id AS Attr_Item_Id,
CONCAT_LIST(
CAST(
COLLECT(
La.Attribute_Name || '|~|' || Lav.Attribute_Value ||' '|| Lau.Attribute_Uom
ORDER BY ICA.DISP_SEQ,LA.ATTRIBUTE_NAME
)
AS stringlist
),
'}~}'
) AS ATTR
FROM your_table
GROUP BY iav.item_id;
LISTAGG is limited to 4000 characters unfortunately. So you may want to use another approach to concatenate the values.
Anyway ...
It is strange to see LISTAGG which is a rather new feature combined with error-prone SQL1992 joins. I'd suggest you re-write this. Are the tables even properly joined? It looks strange that there seems to be no relation between Loc_Attributes and, say, Loc_Attribute_Values. Doesn't have Loc_Attribute_Values a Loc_Attribute_Id so an attribute value relates to an attribute? It would be hard to believe that there is no such relation.
Moreover: Is it guaranteed that your classification subquery doesn't return more than one record per attribute?
Here is your query re-written:
select
iav.item_id as attr_item_id,
listagg(la.attribute_name || '|~|' || lav.attribute_value || ' ' || lau.attribute_uom,
'}~}') within group (order by ica.disp_seq, la.attribute_name) as attr
from item_attribute_values iav
join loc_attribute_values lav
on lav.loc_attribute_value_id = iav.loc_attribute_value_id
and lav.loc_attribute_id = iav.loc_attribute_id -- <== maybe?
join loc_attributes la
on la.loc_attribute_id = lav.loc_attribute_id
and la.loc_attribute_id = lav.loc_attribute_id -- <== maybe?
and la.locale_id = 1
left join loc_attribute_uoms lau
on lau.loc_attribute_uom_id = iav.loc_attribute_uom_id
and lau.loc_attribute_id = iav.loc_attribute_id -- <== maybe?
left join
(
-- aggregation needed to get no more than one sortkey per item attribute?
select ic.item_id, ca.attribute_id, min (ca.disp_seq) as disp_seq
from item_classification ic
join category_attributes ca on ca.taxonomy_tree_id = ic.taxonomy_tree_id
where ic.default_category = 'y'
group by ic.item_id, ca.attribute_id
) ica on ica.item_id = iav.item_id and ica.attribute_id = iav.attribute_id
group by iav.item_id;
Well, you get the idea; check your keys and alter your join criteria where necessary. Maybe this gets rid of duplicates, so LISTAGG has to concatenate less attributes, and maybe the result even stays within 4000 characters.
Xquery approach.
Creating extra types or function isn't necessary.
with test_tab
as (select object_name
from all_objects
where rownum < 1000)
, aggregate_to_xml as (select xmlagg(xmlelement(val, object_name)) xmls from test_tab)
select xmlcast(xmlquery('for $row at $idx in ./*/text() return if($idx=1) then $row else concat(",",$row)'
passing aggregate_to_xml.xmls returning content) as Clob) as list_in_lob
from aggregate_to_xml;
I guess you need to write a small function to concatenate the strings into a CLOB, because even when you cast TO_CLOB() the LISTAGG at the end, this might not work.
HereĀ“s a sample-function that takes a SELECT-Statement (which MUST return only one string-column!) and a separator and returns the collected values as a CLOB:
CREATE OR REPLACE FUNCTION listAggCLob(p_stringSelect VARCHAR2
, p_separator VARCHAR2)
RETURN CLOB
AS
cur SYS_REFCURSOR;
s VARCHAR2(4000);
c CLOB;
i INTEGER;
BEGIN
dbms_lob.createtemporary(c, FALSE);
IF (p_stringSelect IS NOT NULL) THEN
OPEN cur FOR p_stringSelect;
LOOP
FETCH cur INTO s;
EXIT WHEN cur%NOTFOUND;
dbms_lob.append(c, s || p_separator);
END LOOP;
END IF;
i := length(c);
IF (i > 0) THEN
RETURN dbms_lob.substr(c,i-length(p_separator));
ELSE
RETURN NULL;
END IF;
END;
This function can be used f.e. like this:
WITH cat AS (
SELECT DISTINCT t1.category
FROM lookup t1
)
SELECT cat.category
, listAggCLob('select t2.name from lookup t2 where t2.category = ''' || cat.category || '''', '|') allcategorynames
FROM cat;

Oracle RLS/VPD with for loop in policy function

This is a followup to an older question about Oracle's row level security feature originally discussed here
I needed to modify the because the original code would return more than one results if the user was attached to multiple projects. So now I need to have multiple where conditions (ie where project = project_1 or project = project_2) passed to the security policy for this to work. To do this I tried modifying the code by using a for loop and it is not working...
--create function
create or replace function
table_access_policy
(obj_schema varchar2, obj_name varchar2) return varchar2
is
v_project_temp varchar2(9000);
begin
v_project_temp:= 'declare v_project varchar2(9000);
begin
v_project:= ''project = '';
for c in (select admin.access_list.project from admin.access_list where upper(admin.access_list.user_id) = SYS_CONTEXT (''USERENV'', ''SESSION_USER''))
loop
v_project := v_project || c.project_sn || '' or project = '' ;
end loop;
v_project := rtrim(v_project, '' or project = '');
end;';
return v_project_temp;
end;
The function saves/runs without any errors, but the policy itself throws an error when it's called. Is there a better way to do this?
Instead of putting the PL/SQL in a string you should run it and build up the v_project string to return. Such as:
--create function
create or replace function
table_access_policy
(obj_schema varchar2, obj_name varchar2) return varchar2
is
v_project varchar2(9000);
begin
v_project:= 'project = ';
for c in (select admin.access_list.project from admin.access_list where upper(admin.access_list.user_id) = SYS_CONTEXT ('USERENV', 'SESSION_USER'))
loop
v_project := v_project ||''''|| c.project_sn ||''''|||| ' or project = ' ;
end loop;
v_project := rtrim(v_project, ' or project = ');
return v_project;
end;
Ultimately the value that appears in v_project will go straight after a where in an SQL statement such as:
select * from data;
will become
select * from date where <v_project>;
So only something that follows a where should go in v_project.

How to pass a record as parameter for PL/pgSQL function?

I keep looking for this answer online but I cannot find it.
I am trying to pass one record over a PL/pgSQL function. I tried it in two ways.
Fist way :
CREATE OR REPLACE FUNCTION translateToReadableDate(mRecord dim_date%ROWTYPE) RETURNS void AS $$
That is the ouput :
psql:requestExample.sql:21: ERROR: syntax error at or near "%"
LINE 1: ... FUNCTION translateToReadableDate(mRecord dim_date%ROWTYPE) ...
^
Second way :
CREATE OR REPLACE FUNCTION translateToReadableDate(mRecord RECORD) RETURNS void AS $$
And there is the output
psql:requestExample.sql:21: ERROR: PL/pgSQL functions cannot accept type record
Someone does know how to do this please ?
CREATE OR REPLACE FUNCTION translateToReadableDate(mRecord dim_date) RETURNS void AS $$
BEGIN
SELECT dim_day.name || ' (' || dim_day_in_month.id || ') ' || dim_month.name || 'is the ' || dim_week.id || ' week of the year. ' AS "Une phrase", dim_quarter.id, dim_year.id
FROM dim_date dd
JOIN dim_day ON dd.day_id = dim_day.day_id
JOIN dim_day_in_month ON dd.day_in_month_id = day_in_month.day_in_month_id
JOIN dim_week ON dd.week_id = dim_week.week_id
JOIN dim_month ON dd.month_id = dim_month.month_id
JOIN dim_quarter ON dd.quarter_id = dim_quarter.quarter_id
JOIN dim_year ON dd.year_id = dim_year.year_id
WHERE dd.day_id = mRecord.day_id
AND dd.day_in_month_id = mRecord.day_in_month_id
AND dd.week_id = mRecord.week_id
AND dd.month_id = mRecord.month_id
AND dd.quarter_id = mRecord.quarter_id
AND dd.year_id = mRecord.year_id;
END;
$$ LANGUAGE plpgsql;
Try this:
CREATE OR REPLACE FUNCTION translateToReadableDate(mRecord dim_date) RETURNS void AS $$
dim_date must be a table.
EDIT:
Ok, now I'm really really confused.
A date should be a column, not a table. I can't understand why would you create a table with date values.
You can format dates no problem with to_char. Read this: Data Type Formatting Functions to learn how to. That function you created makes zero sense.
Are you outputting PL/pgSQL? Shouldn't the formatting be done by the middle tier? You should just return a Date from the database.
Lastly, I would recommend reading the PL/pgSQL Manual. There's lots of good stuff in there.