return 0 in select count when no record found - sql

i'm trying to get an id , and a count in same query result.
the problem is when the record doesn't exist , the count return null instead of 0
this is the query
SELECT DISTINCT Id
,(
SELECT count(*)
FROM table1
WHERE reference_id = 300000009798620
)
FROM table1
WHERE reference_id = 300000009798620;

Just use:
SELECT max(id) as id, count(*)
FROM table1
WHERE reference_id = 300000009798620

Please try the below modified query
SELECT 300000009798620 as reference_id, count(*)
FROM table1 WHERE reference_id = 300000009798620

Would this do?
table1 CTE represents your table; I simplified REFERENCE_ID. Both rows share the same reference_id value (1), with two different ID column values (1 and 2)
temp1 selects IDs for par_reference_id parameter value
temp2 counts rows for par_reference_id
First execution returns something as par_reference_id = 1 (count is 2):
SQL> with
2 table1 (id, reference_id) as
3 -- this represents your TABLE1 (but reference_id is way simpler)
4 (select 1, 1 from dual union all
5 select 2, 1 from dual),
6 temp_1 as
7 -- distinct IDs per desired reference_id
8 (select distinct id
9 from table1
10 where reference_id = &&par_reference_id
11 ),
12 temp_2 as
13 -- number of rows for that reference_id
14 (select count(*) cnt
15 from table1
16 where reference_id = &&par_reference_id
17 )
18 -- and finally:
19 select b.id, a.cnt
20 from temp_2 a left join temp_1 b on 1 = 1;
Enter value for par_reference_id: 1
ID CNT
---------- ----------
1 2
2 2
Let's try some other reference_id value (which doesn't exist in table1), e.g. 100: query doesn't return any ID, but count = 0 (as you wanted):
SQL> undefine par_reference_id
SQL> /
Enter value for par_reference_id: 100
ID CNT
---------- ----------
0
SQL>

You can use DUAL to always get a row back with your ID, then a subquery to get the count.
SELECT 300000009798620 AS id,
(SELECT COUNT (*)
FROM table1
WHERE reference_id = 300000009798620) AS amt
FROM DUAL;

Related

ORACLE get rows with condition value equals something but not equals to anything else

I have rows that look like .
OrderNo OrderStatus SomeOtherColumn
A 1
A 1
A 3
B 1 X
B 1 Y
C 2
C 3
D 2
I want to return all orders that have only one possible value of orderstatus. For e.g Here order B has only order status 1 SO result should be
B 1 X
B 1 Y
Notes:
Rows can be duplicated with same order status. For e.g. B here.
I am interested in the order having a very peculiar status for e.g. 1 here and not having any other status. So if B had a status of 3 at any point of time it is disqualified.
You can use not exists:
select t.*
from t
where not exists (select 1
from t t2
where t.orderno = t2.orderno and t.OrderStatus = t2.OrderStatus
);
If you just want the orders where this is true, you can use group by and having:
select orderno
from t
group by orderno
having min(OrderStatus) = max(OrderStatus);
If you only want a status of 1 then add max(OrderStatus) = 1 to the having clause.
Here is one way to do it. It does not handle the case where the status can be NULL; if that is possible, you will need to explain how you want it handled.
SQL> create table test_data ( orderno, status, othercol ) as (
2 select 'A', 1, null from dual union all
3 select 'A', 1, null from dual union all
4 select 'A', 3, null from dual union all
5 select 'B', 1, 'X' from dual union all
6 select 'B', 1, 'Y' from dual union all
7 select 'C', 2, null from dual union all
8 select 'C', 3, null from dual union all
9 select 'D', 2, null from dual
10 );
Table created.
SQL> variable input_status number
SQL> exec :input_status := 1
PL/SQL procedure successfully completed.
SQL> column orderno format a8
SQL> column othercol format a8
SQL> select orderno, status, othercol
2 from (
3 select t.*, count(distinct status) over (partition by orderno) as cnt
4 from test_data t
5 )
6 where status = :input_status
7 and cnt = 1
8 ;
ORDERNO STATUS OTHERCOL
-------- ---------- --------
B 1 X
B 1 Y
One way to handle NULL status (if that may happen), if in that case the orderno should be rejected (not included in the output), is to define the cnt differently:
count(case when status != :input_status or status is null then 1 end)
over (partition by orderno) as cnt
and in the outer query change the WHERE clause to a single condition,
where cnt = 0
Count distinct OrderStatus partitioned by OrderNo and show only rows where number equals one:
select OrderNo, OrderStatus, SomeOtherColumn
from ( select t.*, count(distinct orderstatus) over (partition by orderno) cnt
from t )
where cnt = 1
SQLFiddle demo
Just wanted to add something to Gordon's answer, using a stats function:
select orderno
from t
group by orderno
having variance(orderstatus) = 0;

Select rows not in another table by comparing two table

I have following two tables TableA and TableB
TableA
Id Month_Id Customer_Id Total_Amount
1 1 1 50
2 2 1 150
3 3 1 200
4 1 2 75
5 2 2 100
6 1 3 400
7 2 3 200
TableB
Id Month_Id Customer_Id Total_Amount
1 1 1 50
2 2 1 150
3 1 2 75
I want to compare Month_Id Customer_Id Total_Amount in both tables and select Id from TableA. The output should be as follow.
Output
Id
3
5
6
7
My concept is:
SELECT TableA.Id FROM TableA
WHERE TableA.Month_Id <> TableB.MonthId AND
TableA.Customer_Id <> TableB.Customer_Id AND
TableA.Total_Amount <> TableB.Total_Amount
SELECT TableA.Id
FROM TableA
WHERE NOT EXISTS (
SELECT 1
FROM TableB
WHERE TableB.Month_Id = TableA.Month_Id
AND TableB.Customer_Id = TableA.Customer_Id
AND TableB.Total_Amount = TableA.Total_Amount
)
select Id
from (
select Id, Month_Id, Customer_Id, Total_Amount from TableA
except
select Id, Month_Id, Customer_Id, Total_Amount from TableB
) q
SELECT id FROM
(SELECT id, month_id, customer_id, total_ammount FROM TableA
EXCEPT
SELECT id, month_id, customer_id, total_ammount FROM TableB);
You can use the EXCEPT set operator:
SELECT id
FROM (SELECT * FROM table_a
EXCEPT
SELECT * FROM table_b) t
You can use Merge with WHEN NOT MATCHED
place your condition in ON <merge_search_condition>
SELECT Id FROM TableA A LEFT JOIN tableB B
ON A.Id=B.Id AND A.Month_Id =B.Month_Id
AND A.Customer_Id =B.Customer_Id
AND A.Total_Amount=b.Total_Amount
WHERE B.Id is NULL
In oracle sql it would be:
SELECT ID FROM
(SELECT ID, Month_Id, Customer_Id, Total_Amount FROM TABLE_A
MINUS
SELECT ID, Month_Id, Customer_Id, Total_Amount FROM TABLE_B);
Is this what you want?
(Not sure of MINUS operator in sql-server though)

Get next row value based on returned list of rows

In Oracle, suppose I have a query that returns the following list:
ID Sequence#
12 1
15 3
25 5
All I know in this case is the ID of some row (let's suppose 12), I need to return the ID of a row with the next sequence number which in this case is 3 (id = 15)
How can I do it? I know there's a Oracle function lead, but I wasn't able to successfully impement is.
Yes, you can use lead function to get the next value. Here is an example of how it can be done.
-- sample of data from your question
SQL> with t1(ID, Sequence#) as
2 (
3 select 12, 1 from dual union all
4 select 15, 3 from dual union all
5 select 25, 5 from dual
6 )
7 select *
8 from (select id
9 , sequence#
10 , lead(sequence#) over(order by id) next_sequence#
11 , lead(id) over(order by id) next_id#
12 from t1
13 )
14 where id = 12
15 ;
ID SEQUENCE# NEXT_SEQUENCE# NEXT_ID#
---------- ---------- -------------- ----------
12 1 3 15
SELECT * FROM table1 where ID in (SELECT min(ID) FROM table1 WHERE ID > 12)
Select sequence from my_ table where id=(select min(id) from my_table where sequence> 1)
Replace (1) in the above query with any value that you are searching for its next

oracle intersection with or without distinct

I have following query:
select id from t1
intersect
select id from t2
intersect
select id from t3
id could be non unique in some tables, so I need to use distinct.
In general what is better:
select distinct id from (
select id from t1
intersect
select id from t2
intersect
select id from t3)
or
select distinct id from t1
intersect
select id from t2 -- here id is unique
intersect
select distinct id from t3
There is no need for the DISTINCT. The INTERSECT operator automatically produces a distinct set of values. As you can see in this example, x has two rows with an ID of 1, y only has one row with an ID of 1 and the INTERSECTION of the two produces just a single row
SQL> ed
Wrote file afiedt.buf
1 with x as (select 1 id from dual union all select 1 from dual),
2 y as (select 1 id from dual)
3 select id
4 from x
5 intersect
6 select id
7* from y
SQL> /
ID
----------
1
Even if you take the INTERSECT of the two row table with itself, you still get a single row in the output
SQL> ed
Wrote file afiedt.buf
1 with x as (select 1 id from dual union all select 1 from dual)
2 select id
3 from x
4 intersect
5 select id
6* from x
SQL> /
ID
----------
1

In Oracle, how do I get a page of distinct values from sorted results?

I have 2 columns in a one-to-many relationship. I want to sort on the "many" and return the first occurrence of the "one". I need to page through the data so, for example, I need to be able to get the 3rd group of 10 unique "one" values.
I have a query like this:
SELECT id, name
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
ORDER BY name, id;
There can be multiple rows in table2 for each row in table1.
The results of my query look like this:
id | name
----------------
2 | apple
23 | banana
77 | cranberry
23 | dark chocolate
8 | egg
2 | yak
19 | zebra
I need to page through the result set with each page containing n unique ids. For example, if start=1 and n=4 I want to get back
2
23
77
8
in the order they were sorted on (i.e., name), where id is returned in the position of its first occurrence. Likewise if start=3 and n=4 and order = desc I want
8
23
77
2
I tried this:
SELECT * FROM (
SELECT id, ROWNUM rnum FROM (
SELECT DISTINCT id FROM (
SELECT id, name
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
ORDER BY name, id)
WHERE ROWNUM <= 4)
WHERE rnum >=1)
which gave me the ids in numerical order, instead of being ordered as the names would be.
I also tried:
SELECT * FROM (
SELECT DISTINCT id, ROWNUM rnum FROM (
SELECT id FROM (
SELECT id, name
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
ORDER BY name, id)
WHERE ROWNUM <= 4)
WHERE rnum >=1)
but that gave me duplicate values.
How can I page through the results of this data? I just need the ids, nothing from the "many" table.
update
I suppose I'm getting closer with changing my inner query to
SELECT id, name, rank() over (order by name, id)
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
...but I'm still getting duplicate ids.
You may need to debug it a little, but but it will be something like this:
SELECT * FROM (
SELECT * FROM (
SELECT id FROM (
SELECT id, name, row_number() over (partition by id order by name) rn
FROM table1
INNER JOIN table2 ON table2.fkid = table1.id
)
) WHERE rn=1 ORDER BY name, id
) WHERE rownum>=1 and rownum<=4;
It's a bit convoluted (and I would tend to suspect that it could be simplified) but it should work. You'd can put whatever start and end position you want in the WHERE clause-- I'm showing here with start=2 and n=4 are pulled from a separate table but you could simplify things by using a couple of parameters instead.
SQL> ed
Wrote file afiedt.buf
1 with t as (
2 select 2 id, 'apple' name from dual union all
3 select 23, 'banana' from dual union all
4 select 77, 'cranberry' from dual union all
5 select 23, 'dark chocolate' from dual union all
6 select 8, 'egg' from dual union all
7 select 2, 'yak' from dual union all
8 select 19, 'zebra' from dual
9 ),
10 x as (
11 select 2 start_pos, 4 n from dual
12 )
13 select *
14 from (
15 select distinct
16 id,
17 dense_rank() over (order by min_id_rnk) outer_rnk
18 from (
19 select id,
20 min(rnk) over (partition by id) min_id_rnk
21 from (
22 select id,
23 name,
24 rank() over (order by name) rnk
25 from t
26 )
27 )
28 )
29 where outer_rnk between (select start_pos from x) and (select start_pos+n-1 from x)
30* order by outer_rnk
SQL> /
ID OUTER_RNK
---------- ----------
23 2
77 3
8 4
19 5