Oracle - NVL, cascading NVLs, and COALESCE not working as expected - oracle

I want this field cost_total to return 0 if it's null. But for some reason it's still returning null on the front end. I've tried:
SUM(ROUND(NVL(RAC.NL_COST, 0),2)) OVER() cost_total
NVL(SUM(ROUND(RAC.NL_COST,2)) OVER(), 0) cost_total
COALESCE(SUM(ROUND(RAC.NL_COST,2)), 0) OVER() cost_total
SUM(ROUND(COALESCE(RAC.NL_COST, 0),2)) OVER(), 0) cost_total
Why are these still returning null and not 0?
I've also tried these to no avail:
NVL(SUM(ROUND(NVL(RAC.NL_COST, 0),2)), ' ') OVER() cost_total
CASE SUM(ROUND(NVL(RAC.NL_COST,0),2)) OVER() WHEN 0 THEN ' ' END cost_total

Try this:
NVL(ROUND(SUM(RAC.NL_COST) OVER (), 2), 0)
Cheers!!

Related

Oracle ORA -932 expected Number got CHAR

I am running the following query and can't seem to figure out where the error is-
select case when month>=7 then substr(year,3,2)|| '/'||TO_NUMBER( substr(year,3,2))+1
else to_number(substr(year,3,2))-1 || '/'|| substr(year,3,2) end as fiscal_year
FROM ( SELECT DISTINCT to_Char(extract( year from date)) as year,
extract( month from date)as MONTH FROM TABLE )
I want to convert year to fiscal year like 19/20, 20/21 etc
The operator precedence rules means the string concatenation is happening before the addition; this expression:
substr(year,3,2)|| '/'||TO_NUMBER( substr(year,3,2))+1
is evaluated as
substr(year,3,2)|| '/'||TO_NUMBER( substr(year,3,2))
and then it tries to add 1 to that string result. Hence the error you get.
You can add parentheses to make it add 1 to the year number before then concatenating that:
substr(year,3,2)|| '/'|| (TO_NUMBER( substr(year,3,2))+1)
You could also do this without so much string manipulation:
select case when extract (month from your_date) >= 7 then
to_char(your_date, 'YY') || '/' || to_char(add_months(your_date, 12), 'YY')
else
to_char(add_months(your_date, -12), 'YY') || '/' || to_char(your_date, 'YY')
end as fiscal_year
FROM (
SELECT DISTINCT trunc(your_date, 'MM') as your_date
FROM your_table
)
db<>fiddle
and there are other options, of course.

Condense AND clause Oracle sql

I've come across this in one of the sql scripts we have for one of our apps. I notice that it's used in various other places, but isn't it just checking for an existence of an item?
AND INSTR((SELECT (',' || REPLACE('OWN, JO', ' ', NULL) || ',') b FROM DUAL),
(',' || aao.AcctRoleCd || ',')) > 0
Where it's looking to see if 'OWN' or 'JO' is in aao.AcctRoleCd. If it is then INSTR would result in its index in the string, so it'll be greater than one. So the AND clause would be true.
Isn't this poor to check if an item exists like this? Would something more of the lines of an IN clause be better?
AND aao.AcctRoleCd IN ('OWN', 'JO');
Almost:
'OWN, JO' is a text literal.
REPLACE('OWN, JO', ' ', NULL) just strips the space from the string giving 'OWN,JO'.
',' || 'OWN,JO' || ',' just concatenates commas to the start and end of the string giving ',OWN,JO,'.
(SELECT ',OWN,JO,') b FROM DUAL) is redundant and you can just use the previous text literal.
INSTR( ',OWN,JO,', (',' || aao.AcctRoleCd || ',') ) > 0 is looking for a comma at the start and end of the substring which equals aao.AcctRoleCd so could match either 'OWN', 'JO' or 'OWN,JO'.
So you can replace it with:
AND aao.AcctRoleCd IN ( 'OWN', 'JO', 'OWN,JO' )
Now, it may be that 'OWN,JO' is not a match you are expecting (or may not even be a valid value) and you can strip it from the list but that is something you will need to determine.
You are right, but thanks for sharing that surprisingly bad code.
(SELECT (',' || REPLACE('OWN, JO', ' ', NULL) || ',') b FROM DUAL)
is a completely unnecessary scalar subquery. It could be replaced by
',' || REPLACE('OWN, JO', ' ', NULL) || ','
However, since that snippet has only literals then it could be further replaced by the result:
,OWN,JO,
and yes, it would seem that whole INSTR could be replaced by the code you suggest, unless aao.AcctRoleCd could contain 'N,J' or some such in which case the original code and your code would get different results. I seriously doubt that is a problem.
Best regards, Stew

Query to search an entire DB for a string

I've been tasked to do a bit of data discovery work. I'm working with our in house application, and I need to determine the first few tables that get hit when we do specific actions in the application. I have no intimate knowledge with the DB other than the fact that there are 1000 different tables I need to account for.
During my search, I came across this link https://lalitkumarb.wordpress.com/2015/01/06/sql-to-search-for-a-value-in-all-columns-of-all-atbles-in-an-entire-schema/
Which is exactly what I need, but when I run it it's not returning any data. I've confirmed that it's not working as intended by going and grabbing some data that I KNOW is in the DB and searching for that.
Here's the query I'm running
SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
SUBSTR (table_name, 1, 14) "Table",
SUBSTR (column_name, 1, 14) "Column"
FROM cols,
TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
|| column_name
|| ' from '
|| table_name
|| ' where upper('
|| column_name
|| ') like upper(''%'
|| :val
|| '%'')' ).extract ('ROWSET/ROW/*') ) )
ORDER BY "Table"
/
When I execute this query, TOAD pops up with a Variables screen in which I specify the type and value of :val. I hit OK, the query executes and returns nothing.
Am I missing something?

Sum Listagg in a query

I am trying to sum the values from the listagg query i made.
Expected result should be like this
SELECT LEGAL_ENTITY_ID, SUM(0 + 0 + 0 + 1) as Grandtotals
FROM V_VBA_DDCR_MAIN WHERE LEGAL_ENTITY_ID=6012346 AND ROWNUM=1
GROUP BY LEGAL_ENTITY_ID
| LEGAL_ENTITY_ID | GRANDTOTALS
1| 6012346 | 1
My listagg goes like this
SELECT LISTAGG(NVL2(FLDNM,0,1) , '+ ') WITHIN GROUP (ORDER BY FLDNM) FROM LE_MERGE_DDC_MAPPING
the purpose of this query is to count the number of null fields in a row. I created a table containing the list of fields that needs to be checked for null values.
Result :
fld1 + fld2 + fld3 + fld4
=
0 + 0 + 0 + 1
So I wrote my query like this:
SELECT LEGAL_ENTITY_ID, SUM(SELECT LISTAGG(NVL2(FLDNM,0,1) , '+ ') WITHIN GROUP
(ORDER BY FLDNM) FROM LE_MERGE_DDC_MAPPING) as Grandtotals
FROM V_VBA_DDCR_MAIN WHERE LEGAL_ENTITY_ID=6000132
GROUP BY LEGAL_ENTITY_ID
However, I am getting an error ORA-00936: missing expression.
I am not sure if Oracle allows you to do a sum of the listagg function.
Any help is appreciated.
Your SELECT LISTAGG(NVL2(FLDNM,0,1) , '+ ')... query will always get string result '0+ 0+ 0+ ...', unless you have rows in LE_MERGE_DDC_MAPPING which are null. That value is nothing to do with the V_VBA_DDCR_MAIN table. You could generate a string that contains the expressions:
SELECT LISTAGG('NVL2(' || FLDNM || ' ,0,1)' , '+ ') ...
But that would just leave you with a string containing 'NVL2(fld1 ,0,1)+ NVL2(fld2 ,0,1)+ ...'. You can't sum a string, and sum() can't evaluate a string as an expression. You would need to generate a dynamic SQL statement based on that string.
There is a way to do that with XML, which isn't entirely intuitive. You can use the dbms_xmlgen package to create an XML document (as a CLOB) which contains a node with the NVL2 result from your actual target table:
select dbms_xmlgen.getxml('select nvl2(' || fldnm || ',0,1) from v_vba_ddcr_main where legal_entity_id=6012346')
from le_merge_ddc_mapping map;
I won't show the generated XML here. You can then query that to get the individual values out:
select xmlquery('/ROWSET/ROW/*/text()'
passing xmltype(
dbms_xmlgen.getxml('select nvl2(' || fldnm || ',0,1) from v_vba_ddcr_main where Legal_Entity_Id=6012346')
)
returning content)
from le_merge_ddc_mapping;
And you can them sum those:
select sum(to_number(xmlquery('/ROWSET/ROW/*/text()'
passing xmltype(
dbms_xmlgen.getxml('select nvl2(' || fldnm || ',0,1) from v_vba_ddcr_main where legal_entity_id=6012346')
)
returning content))) as grandtotals
from le_merge_ddc_mapping;
You've suggested there is a link between the tables, to determine which fields are included, so you'd need to add that. And you've said there are duplicates, which you'd need to handle - preferably by eliminating them from your view, but with a subquery if necessary. This can be a starting point for you to develop from though.
Why use LISTAGG at all?
SELECT LEGAL_ENTITY_ID,
(
SELECT COUNT(1)
FROM LE_MERGE_DDC_MAPPING
WHERE FLDNM IS NULL
) as Grandtotals
FROM V_VBA_DDCR_MAIN
WHERE LEGAL_ENTITY_ID=6000132;
(Since you are filtering on a single LEGAL_ENTITY_ID you don't need the final GROUP BY)
Are you sure there shouldn't be some sort of join condition between the two tables?

Not able to use <> in Function based Indexes in Oracle

We are trying to create a function based index which has <> (not equal to) in the where clause but get an error that say
ORA-00907: missing right parenthesis
Is there any problem in using the <> clause ? Can it be made to work in someway.
CREATE INDEX IX_TEST_TABLE ON TEST_TABLE ((NVL(COL_A, 0) <> NVL(COL_B, 0));
You could use the result of that comparison in a case statement to come up with an actual value, with a supported data type, rather than a boolean - which has been noted already isn't supported y Oracle as an SQL data type. What that value is doesn't really matter as long as you're consistent; you could use Y/N, 0/1, etc.
Depending on your data spread and selectivity of how you'll query, you could use a bitmap index:
create bitmap index ix_test_table on test_table
(case when nvl(col_a, 0) <> nvl(col_b, 0) then 1 else 0 end);
And then query on the same case, of course:
select * from test_table
where case when nvl(col_a, 0) <> nvl(col_b, 0) then 1 else 0 end = 1;
select * from test_table
where case when nvl(col_a, 0) <> nvl(col_b, 0) then 1 else 0 end = 0;
Or if it's very selective only include the small subset of rows that you're interested in by utilising the fact that null values are not included in the index:
create index ix_test_table on test_table
(case when nvl(col_a, 0) <> nvl(col_b, 0) then 1 end);
select * from test_table
where case when nvl(col_a, 0) <> nvl(col_b, 0) then 1 end = 1;
You'd need to evaluate which is appropriate for your data.
In Oracle BOOLEAN is a PL/SQL-only type, so you can't use it in SQL, not in indexes either. the function in your index is resulting in either true or false and is therefore not allowed.

Resources