How to make null equal null in oracle - oracle

I have a variable being passed to my stored proc and it's a filter (basically). However, that field can sometimes be null, and if it is, I want to be able to check against the rows that have that field as null.
For example,
Table A:
VALUE_COLUMN | FILTER_COLUMN
----------------------------
A | (NULL)
B | (NULL)
C | (NULL)
D | (NULL)
A | 1
E | (NULL)
F | (NULL)
B | 1
The query (With inputs, val, filter):
SELECT COUNT(1)
FROM TableA
WHERE
wrap_up_cd = val
AND brn_brand_id = filter
Expected I/O:
val = A, filter = (null) = 1
val = A, filter = 1 = 1
val = C, filter = 1 = 0
How can I make Oracle behave this way?

How about:
SELECT COUNT(1)
FROM TableA
WHERE
wrap_up_cd = val
AND ((brn_brand_id = filter) OR (brn_brand_id IS NULL AND filter IS NULL))
I'm not an Oracle expert, but I'd expect that to work - basically make the query match if both the filter and the value are NULL.

Oracle doesn't have an ISNULL function. So you'd need something like
SELECT COUNT(*)
FROM tableA
WHERE brn_brand_id = filter
OR ( brn_brand_id IS NULL
AND filter IS NULL)

This can also be handy at times:
SELECT COUNT(*)
FROM tableA
WHERE
NVL(brn_brand_id, CHR(0)) = NVL(filter, CHR(0))

Related

Function retuning Sys Refcursor

How to call a function returning sys refcursor in select statement. I have created a function like this and I want to call in the select statement returning both values coming from function. So I used in the query like this, but it is returning cursor in place of column values.
Function HCLT_GET_TASK_DATES(i_ownerid IN NUMBER, i_itemid IN NUMBER)
RETURN SYS_REFCURSOR IS
o_DATACUR SYS_REFCURSOR;
begin
open o_DATACUR for
select nvl(TO_CHAR(min(pref_start), 'DD-MON-YYYY'), '') AS MIN_DATE,
nvl(TO_CHAR(max(pref_finish), 'DD-MON-YYYY'), '') AS MAX_DATE
from autoplanallocation
WHERE project_id = i_ownerid
AND task_id = i_itemid;
RETURN o_DATACUR;
END;
/
SELECT HCLT_GET_TASK_DATES(267157, 15334208),
tv.taskid,
tv.wbs_code AS wbscode,
tv.taskcode,
tv.act_name,
ltrim(regexp_replace(tv.stageactorlovs, '[^#]*#(\d+?),', ',\1'), ',') as stageactorlovs,
tv.createdat,
tv.pushedtoTaskModule,
tv.OVERALLSTATUS AS overallstatus1,
tv.ACTIVITY_CODE_ID,
tv.wbs_code,
TO_CHAR(tv.pref_st, 'DD-MON-YYYY') AS pref_st,
TO_CHAR(tv.pref_fn, 'DD-MON-YYYY') AS pref_fn,
tv.ACTL_EFFORT,
tv.rollup_effort,
tv.overAllStatus,
tv.FIELD5,
tv.FIELD4,
tv.activity_code_id
FROM task_view tv, autoplanallocation al
WHERE al.project_id = tv.ownerid(+)
and al.task_id = tv.taskid(+)
and tv.ownertype = 'Prj'
AND tv.ownerid = 267157
AND (tv.overAllStatus = 'All' OR 'All' = 'All')
AND (TaskId IN
((SELECT xyz
FROM (SELECT ToItemID xyz
FROM ItemTraceability it
WHERE it.FromOwnerType = 'Prj'
AND it.FromOwnerID = 267157
AND it.FromItemType = it.FromItemType
AND it.FromChildItemType = 'USTRY'
AND it.FromItemID = 15334208
AND it.ToOwnerType = 'Prj'
AND it.ToOwnerID = 267157
AND it.ToItemType = it.ToItemType
AND it.ToChildItemType = 'Tsk'
UNION ALL
SELECT FromItemID
FROM ItemTraceability it
WHERE it.ToOwnerType = 'Prj'
AND it.ToOwnerID = 267157
AND it.ToItemType = it.ToItemType
AND it.ToChildItemType = 'USTRY'
AND it.ToItemID = 15334208
AND it.FromOwnerType = 'Prj'
AND it.FromOwnerID = 267157
AND it.FromItemType = it.FromItemType
AND it.FromChildItemType = 'Tsk'))))
ORDER BY UPPER(wbs_code) ASC;
I do not think there is a native way of parsing nested cursors using SQL or PL/SQL code.
In Java with an Oracle JDBC database driver, you can:
Use oracle.jdbc.driver.OraclePreparedStatement.executeQuery to get a java.sql.ResultSet
Which can be cast to an oracle.jdbc.driver.OracleResultSet
Then you can iterate through the rows of the result set and for each row you can use oracle.jdbc.driver.OracleResultSet.getCursor() to get the nested cursor.
You can then iterate through that nested cursor in exactly the same way you iterated through the outer cursor to extract rows from the nested cursor.
You should then close the nested cursor (although it will be automatically closed when the containing parent cursor is closed).
Finally, close the parent cursor.
If you want an SQL solution then do not return a cursor and return a nested table collection data type instead.
Or, for a single row with multiple columns, return an object type:
CREATE TYPE date_range_obj AS OBJECT(
start_date DATE,
end_date DATE
)
/
CREATE FUNCTION HCLT_GET_TASK_DATES(
i_ownerid IN autoplanallocation.project_id%TYPE,
i_itemid IN autoplanallocation.task_id%TYPE
)
RETURN date_range_obj
IS
v_range date_range_obj;
begin
SELECT date_range_obj(MIN(pref_start), MAX(pref_finish))
INTO v_range
FROM autoplanallocation
WHERE project_id = i_ownerid
AND task_id = i_itemid;
RETURN v_range;
END;
/
Then, for example:
SELECT HCLT_GET_TASK_DATES(1,2).start_date,
HCLT_GET_TASK_DATES(1,2).end_date
FROM DUAL;
db<>fiddle here
If you are able to change this design, then it would be better to do in plain join and aggregation (or possibly with left join lateral in case of low cardinality input).
But there's a way to achieve the desired result with plain SQL in 11g and above using the ability of dbms_xmlgen package to process arbitrary cursor. Below is the code:
create table t_lkp (id,dt)
as
select
trunc(level/4 + 1)
, date '2022-01-01' + level
from dual
connect by level < 11
create or replace function f_lkp (
p_id in int
)
return sys_refcursor
as
o_res sys_refcursor;
begin
open o_res for
select
min(dt) as dtfrom
, max(dt) as dtto
from t_lkp
where id = p_id;
return o_res;
end;
/
with a as (
select
level as i,
dbms_xmlgen.getxmltype(
/*ctx doesn't accept sys_refcursor, so we had to create a context*/
ctx => DBMS_XMLGEN.NEWCONTEXT(f_lkp(level))
) as val
from dual
connect by level < 6
)
select
i
, xmlquery(
'/ROWSET/ROW/DTFROM/text()'
passing a.val returning content null on empty
) as dtfrom
, xmlquery(
'/ROWSET/ROW/DTTO/text()'
passing a.val returning content null on empty
) as dtto
from a
I | DTFROM | DTTO
-: | :------------------ | :------------------
1 | 2022-01-02 00:00:00 | 2022-01-04 00:00:00
2 | 2022-01-05 00:00:00 | 2022-01-08 00:00:00
3 | 2022-01-09 00:00:00 | 2022-01-11 00:00:00
4 | null | null
5 | null | null
db<>fiddle here
Please note, that it will open too many cursors in case of large input dataset and parallel processing, which will dramatically consume resourses. So it would be much better to use plain join.

How to pass multiple values to UPDATE or SELECT COUNT(*) statement

I am trying to do a update a record.
Table name is customer
id | name | address | state
---+---------+------------+-------------------------
1 | John | 123 main st | TX
2 | Jack | 678 John st | NJ
3 | Bet | 987 Tx st | NY
4 | Maddy| 9812 Hudson st | CA
5 | ABCD | 9813 Mainly st | PA
My query is like below
UPDATE CUSTOMER c SET c.state = 'CA' WHERE c.id IN (idList);
Where idList is a localVariable that I created and it returns a list of id like 1,3,5
The query is working if do it like
UPDATE CUSTOMER c SET c.state = 'CA' WHERE c.id IN (1,3,5);
It is updating the respective records to CA.
But if I use it as
UPDATE CUSTOMER c SET c.state = 'CA' WHERE c.id IN (idList);
I get the below error. I don't want to pass the list directly as the list might change. I am getting the list of ids using a different command where it returns and assigns to idList as 1,3,5
Error:
ORA-01722: invalid number
ORA-06512: at line 35
01722. 00000 - "invalid number"**
How to solve this? I am writing it as a stored procedure.
I guess idList is a comma separated string with all the ids that you want to update.So what is happening is that the operator IN compares each id with that string and since this comparison can't succeed in any case you get an error.
What you can do instead is use the LIKE operator:
UPDATE customer
SET "state" = 'CA'
WHERE ',' || idList || ',' LIKE '%,' || "id" || ',%'
See the demo.
You may create parameterized query string and pass values like,
UPDATE CUSTOMER c SET c.state = 'CA' WHERE c.id IN (:idList);
You can't substitute a text variable for a list of values - it's simply not allowed. You're going to have to use dynamic SQL:
EXECUTE IMMEDIATE 'UPDATE CUSTOMER c SET c.state = ''CA'' WHERE c.id IN (' || idList || ')';

Making an Oracle query between three tables using INNER JOIN

I'm working with a super inconsistent Oracle database and I need help to make a query.
Simplyfying the database to an example I have these three tables.
TABLE_F
------
id = 3
title = "Hello"
TABLE_M
------
id = 3
category = "val3"
flid = 5
TABLE_X
------
id = 3
body = "How are you?"
flid = 30
id = 3
body= "Bye bye"
flid = 35
I want to make a query to get the following result:
id | title | mat | BODY | OTHER
------------------------------------------
3 helllo val3 How are you? Bye bye
My query is:
SELECT
TABLE_F.title,
TABLE_M.category,
TABLE_X.body as BODY
FROM TABLE_F
INNER JOIN TABLE_M
ON TABLE_F.id=TABLE_M.id
INNER JOIN TABLE_X
ON TABLE_F.id=TABLE_X.id
WHERE TABLE_M.flid=5 AND TABLE_X.flid=30;
Where I get:
id | title | mat | BODY
--------------------------------
3 helllo val3 How are you?
I need to add to the query the TABLE_X.body as OTHER (which contains the "Bye bye" string), BUT I can't do it as I'm filtering witd flid=30 in order to get the body.
It's not my database and I can't change the design. I need to get that desired output with one query (which I dont know if it's possible).
Thanks in advance.
Join table_x two times using alias
SELECT_F.id,
TABLE_F.title,
TABLE_M.category mat,
TABLE_X.body BODY,
Y.body OTHER BODY,
FROM TABLE_F
INNER JOIN TABLE_M ON TABLE_F.id=TABLE_M.id
INNER JOIN TABLE_X ON TABLE_F.id=TABLE_X.id AND TABLE_X.flid=30
INNER JOIN TABLE_X Y ON TABLE_F.id=Y.id AND Y.flid=35

Inner or Outer joins with Pivot query

I have a query that pivots a rows into columns. The rows only exist if values are present, e.g. there are no rows with the values represented by NULL
Object Field_id value
1 1 value1
1 2 value2
1 3 value3
2 2 value4
And I create the output
Object field1 field2 field3
1 value1 value2 value3
2 value4
using a query like
select * from
(
select fs.field_name, s.text_value, s.id_object
from custom_field_str s ,
(select ad.id_field field_id, fd.name field_name,
num_display_order display_order, ad.text_table_name catalogue_table, ad.num_lines
from catalogues c
inner join attribute_definitions ad on c.id_object = ad.id_object_type
inner join field_definitions fd on ad.id_field = fd.id_object
where c.id_object = 'cA1') fs
where fs.catalogue_table = 'custom_field_str'
and fs.field_id = s.id_field
)
pivot
( max(text_value)
for field_name IN ('field1' as field1,'field2' as field2,'field3' as field3) )
So my question is, should I be using an outer join in the join between custom_field_str and the derived table fs. Or does the PIVOT not require a full set of results to build the output grid?
This line: where fs.field_id = s.id_field makes that for each row in table custom_field_str
must exist row in your derived table fs. So you could change your query to simple join-version, here is my try:
SQLFiddle
select * from (
select fd.name field_name, s.text_value, s.id_object
from custom_field_str s
join catalogues c on c.id_object = 'cA1'
join attribute_definitions ad on ad.id_field = s.id_field
and c.id_object = ad.id_object_type and ad.text_table_name = 'custom_field_str'
join field_definitions fd on ad.id_field = fd.id_object )
pivot (max(text_value)
for field_name in ('field1' as field1,'field2' as field2,'field3' as field3))

Pivoting in Linq [duplicate]

This question already has answers here:
Pivot data using LINQ
(5 answers)
Closed 9 years ago.
I am using LINQ-to-Entities, and would like to perform a pivot.
For exampe, I have this table:
| data1 | data2 |
+-------+-------+
| 1 | A |
| 1 | B |
| 2 | P |
| 2 | Q |
| 2 | R |
+---------------+
And I want to pivot it into the following results:
| data1 | first | second | third |
+-------+-------+--------+-------+
| 1 | A | B | NULL |
| 2 | P | Q | R |
+--------------------------------+
I would like to do this in LINQ, without needing to do client-side processing.
I have seen these SO posts, but they do not quite address the above situation (as far as I can tell).
Pivot data using LINQ
Is it possible to Pivot data using LINQ?
Note
I have tried the below, but it complains that I cannot use Skip() on an unordered collection, and I don't see a way to get the group's collapsed 'data2' info sorted.
from item in MyTable
group item by item.data1 into g
select new
{
data1 = g.Key,
first = g.Skip(0).FirstOrDefault().data2,
second = g.Skip(1).FirstOrDefault().data2,
third = g.Skip(2).FirstOrDefault().data2,
};
I assume that you could have more than three columns from the data2 field?
If so there's no way to do you query that returns an anonymous type with a variable number of properties. You need to return an array or some sort of list for the data2 set of values.
I think this is the kind of thing that you can do:
var query =
from mt in MyTable
group mt.data2 by mt.data1 into gmts
let d2 = gmts.ToArray()
select new
{
data1 = gmts.Key,
data2 = d2,
length = d2.Length,
};
var pending = query.ToArray();
var maxLength = pending.Max(p => p.length);
Func<string[], string[]> extend = xs =>
{
var r = new string[maxLength];
xs.CopyTo(r, 0);
return r;
};
var results =
from p in pending
select new
{
p.data1,
data2 = extend(p.data2),
};
This produces a series of anonymous type with the data2 array all being the same size to fit the maximum number of results for any of the data1 fields.
The query is still executed as a single SQL query. And the in-memory processing is fast.
Does this work for you?
EDIT
Since you know you have a fixed number of columns (as per comment) you can easily change my results query to meet your requirements:
var results =
from p in pending
let d2s = extend(p.data2)
select new
{
p.data1,
first = d2s[0],
second = d2s[1],
third = d2s[2],
};
Hmm, this seems to work, though I wonder how efficient it is.
from item in MyTable
group item by item.data1 into g
select new
{
data1 = g.Key,
first = g.OrderBy(x => x.data2).Skip(0).FirstOrDefault().data2,
second = g.OrderBy(x => x.data2).Skip(1).FirstOrDefault().data2,
third = g.OrderBy(x => x.data2).Skip(2).FirstOrDefault().data2,
};
The corresponding SQL generated (from LINQPad) is:
SELECT [t1].[data1], (
SELECT [t5].[data2]
FROM (
SELECT TOP (1) [t4].[data2]
FROM (
SELECT [t3].[data2], [t3].[ROW_NUMBER]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t2].[data2]) AS [ROW_NUMBER], [t2].[data2]
FROM [MyTable] AS [t2]
WHERE [t1].[data1] = [t2].[data1]
) AS [t3]
WHERE [t3].[ROW_NUMBER] > #p0
) AS [t4]
ORDER BY [t4].[ROW_NUMBER]
) AS [t5]
) AS [first], (
SELECT [t10].[data2]
FROM (
SELECT TOP (1) [t9].[data2]
FROM (
SELECT [t8].[data2], [t8].[ROW_NUMBER]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t7].[data2]) AS [ROW_NUMBER], [t7].[data2]
FROM (
SELECT [t6].[data2]
FROM [MyTable] AS [t6]
WHERE [t1].[data1] = [t6].[data1]
) AS [t7]
) AS [t8]
WHERE [t8].[ROW_NUMBER] > #p1
) AS [t9]
ORDER BY [t9].[ROW_NUMBER]
) AS [t10]
) AS [second], (
SELECT [t15].[data2]
FROM (
SELECT TOP (1) [t14].[data2]
FROM (
SELECT [t13].[data2], [t13].[ROW_NUMBER]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t12].[data2]) AS [ROW_NUMBER], [t12].[data2]
FROM (
SELECT [t11].[data2]
FROM [MyTable] AS [t11]
WHERE [t1].[data1] = [t11].[data1]
) AS [t12]
) AS [t13]
WHERE [t13].[ROW_NUMBER] > #p2
) AS [t14]
ORDER BY [t14].[ROW_NUMBER]
) AS [t15]
) AS [third]
FROM (
SELECT [t0].[data1]
FROM [MyTable] AS [t0]
GROUP BY [t0].[data1]
) AS [t1]

Resources