Counting null dates in Oracle - oracle

I have a question with null dates in Oracle
I've got a table like this:
ID DATE
1 '02/08/2015'
1 NULL
1 '02/06/2014'
2 NULL
2 '06/02/2013'
This is just an example of the real table. Now what I need is something like this:
ID DAY_INAC
1 1
2 1
I mean, I need to count only the null values present in the DATE column.
But when I execute my query
Select id, count(date)
from table
where date is null
group by Id
having count(date)>0
As a result I'm getting nothing. What do I need to with the date value in order to generate the corresponding counting.
Regards

Because your query is already filtering by date is null, you just need to use count(*)
Select id, count(*)
from table
where date is null
group by Id
having count(*) > 0

COUNT does not count NULL values. You can use CASE to change them:
Select id, count(CASE WHEN date IS NULL THEN 1 END) AS DAY_INAC
from table
where date is null
group by Id;
LiveDemo
Please do not name column as datatypes. Otherwise you may need to quote them.

A more condensed query:
Select id, count(nvl2(date_column,null, sysdate)) as cnt
from table
group by Id;

COUNT will not count NULL values - instead get it to count a literal value (i.e. 1) for those rows that are NULL:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( ID, "DATE" ) AS
SELECT 1, DATE '2015-08-02' FROM DUAL
UNION ALL SELECT 1, NULL FROM DUAL
UNION ALL SELECT 1, DATE '2014-06-02' FROM DUAL
UNION ALL SELECT 2, NULL FROM DUAL
UNION ALL SELECT 2, DATE '2013-02-06' FROM DUAL
Query 1:
SELECT id,
COUNT(1)
FROM table_name
WHERE "DATE" IS NULL
GROUP BY id
Results:
| ID | COUNT(1) |
|----|----------|
| 1 | 1 |
| 2 | 1 |

Related

Oracle Select statement without using listagg

DB : Oracle 11G
Below is a query subset result in which I want to fetch only ID =1, also i want to display if this value is there in the result
NAME = 'S123', if its there return the output as an indicator in the query result
ID, NAME,....(other columns)
1, 'I123',...
3, 'S123',...
4, 'W123',...
6, 'C123',...
....,
....,
eg., output
ID, NAME , value_present_ind('S123')
1, 'I123', 'Y'
without using sub-queries on the select statement
without using listagg
WITH demo_data AS ( SELECT 1 AS ID, 'I123' AS NAME FROM DUAL UNION ALL
SELECT 3 AS ID, 'S123' AS NAME FROM DUAL UNION ALL
SELECT 4 AS ID, 'W123' AS NAME FROM DUAL UNION ALL
SELECT 5 AS ID, 'C123' AS NAME FROM DUAL )
SELECT MAX(CASE WHEN id = 1 THEN id ELSE 0 END) AS id,
MAX(CASE WHEN id = 1 THEN name ELSE NULL END) AS name,
MAX(CASE WHEN name = 'I123' THEN 'Y' ELSE 'N' END) AS value_present_ind
FROM demo_data;

rewrite query without DENSE_RANK

I have one very slow query and try to optimize response time by using a materialized view. But one part is not compatible with General Restrictions on Fast Refresh.
How to rewrite it without DENSE_RANK?
create table t (id,object_id,log_cre_date) as
select 1,2,to_date('18/5/2010, 08:00','dd/mm/yyyy, hh:mi') from dual union all
select 2,2,to_date('18/5/2010, 10:00','dd/mm/yyyy, hh mi') from dual union all
select 3,3,to_date('18/5/2010, 11:00','dd/mm/yyyy, hh mi') from dual union all
select 4,3,to_date('18/5/2010, 12:10','dd/mm/yyyy, hh mi') from dual union all
select 5,4,to_date('18/5/2010, 12:20','dd/mm/yyyy, hh mi') from dual union all
select 6,4,to_date('18/5/2010, 11:30','dd/mm/yyyy, hh mi') from dual;
SELECT
MAX(t.id) KEEP(DENSE_RANK FIRST ORDER BY log_cre_date ASC) id,
t.object_id
FROM
t
GROUP BY
t.object_id
I am not sure the accepted answer is fast refreshable. Here is a query that definitely is:
SELECT max(cast(to_char(t.log_cre_date,'YYYYMMDDHH24MISS') || lpad(t.id,30,'0') as varchar2(80))) maxid,
t.object_id,
COUNT(*) cnt
FROM t
GROUP BY t.object_id;
The idea is to append the id to the log_cre_date and take the max of the concatenation. That way, you can extract the id you need later.
So, to get the id, you would do this:
SELECT to_char(substr(maxid,-30)) id, object_id
FROM your_materialized_view;
You could put that in a view to hide the complexity.
Here is a full example:
Create the base table
DROP TABLE t;
create table t (id,object_id,log_cre_date) as
select 1,2,to_date('18/5/2010, 08:00','dd/mm/yyyy, hh:mi') from dual union all
select 2,2,to_date('18/5/2010, 10:00','dd/mm/yyyy, hh mi') from dual union all
select 3,3,to_date('18/5/2010, 11:00','dd/mm/yyyy, hh mi') from dual union all
select 4,3,to_date('18/5/2010, 12:10','dd/mm/yyyy, hh mi') from dual union all
select 5,4,to_date('18/5/2010, 12:20','dd/mm/yyyy, hh mi') from dual union all
select 6,4,to_date('18/5/2010, 11:30','dd/mm/yyyy, hh mi') from dual;
Add some constraints to allow fast-refresh MV
ALTER TABLE t MODIFY id NOT NULL;
ALTER TABLE t ADD CONSTRAINT t_pk PRIMARY KEY ( id );
Create a snapshot log to enable fast refresh
--DROP MATERIALIZED VIEW LOG ON t;
CREATE MATERIALIZED VIEW LOG ON t WITH ROWID, PRIMARY KEY (OBJECT_ID, LOG_CRE_DATE) INCLUDING NEW VALUES;
Create the materialized view (note presence of COUNT(*) in select-list. Important!
--DROP MATERIALIZED VIEW t_mv;
CREATE MATERIALIZED VIEW t_mv
REFRESH FAST ON COMMIT AS
SELECT max(cast(to_char(t.log_cre_date,'YYYYMMDDHH24MISS') || lpad(t.id,30,'0') as varchar2(80))) maxid,
t.object_id,
COUNT(*) cnt
FROM t
GROUP BY t.object_id;
Test it out
select to_number(substr(maxid,-30)) id, object_id
from t_mv;
+----+-----------+
| ID | OBJECT_ID |
+----+-----------+
| 2 | 2 |
| 4 | 3 |
| 5 | 4 |
+----+-----------+
DELETE FROM t WHERE id = 5;
COMMIT;
select to_number(substr(maxid,-30)) id, object_id
from t_mv;
+----+-----------+
| ID | OBJECT_ID |
+----+-----------+
| 4 | 3 |
| 5 | 4 |
| 1 | 2 | -- Now ID #1 is the latest for object_id 2
+----+-----------+
Maybe this query will run faster:
select object_id, id
from (
select object_id, first_value(id) over(partition by object_id order by log_cre_date) as id
from t
)
group by object_id, id;
Hope it helps!
I went through the restriction but I am not sure if following query will work or not.
Try this and let us know if it works.
Select t.id, t.object_id from
T join
(SELECT
min(log_cre_date) mindt,
t.object_id
FROM
t
GROUP BY
t.object_id) t1
On t.object_id = t1.object_id
And t.log_cre_date = t1.mindt;
Cheers!!

Oracle - Selecting not null result of calculation

For every row of my data set, there exist data for the only one of the two options for calculation and the other columns are Null.
My goal is to find simplest way to select not null result of calculation for each row. Expected result:
ROW_NUM result
-------- -------
1 4.5
2 4.56
My code:
With DATASET AS (
-- column 1 is just for row number,
-- column 2 and 3 for caculation option1,
--- columns 4~6 for caculation option2
SELECT 1 ROW_NUM, NULL time1, NULL qty1, 2 time2_1, 2.5 time2_2, 1 qty2
FROM DUAL
UNION
SELECT 2 ROW_NUM, 4.56 time1, 1 qty1, NULL time2_1, NULL time2_2, NULL qty2
FROM DUAL
)
SELECT ROW_NUM, time1/qty1 OPTION1, (time2_1+time2_2)/qty2 OPTION2
FROM DATASET;
Result:
ROW_NUM OPTION1 OPTION2
-------- ------- ---------
1 4.5
2 4.56
You can decode and use different representation when null:
SELECT ROW_NUM, decode(time1/qty1,null,(time2_1+time2_2)/qty2,time1/qty1) result FROM DATASET;
Or nvl
SELECT ROW_NUM, nvl(time1/qty1,(time2_1+time2_2)/qty2,time1/qty1) result FROM DATASET;
NVL lets you replace null (returned as a blank) with a string in the results of a query.
use COALESCE function as following:
With DATASET AS (
--each row contain information for either option1 or 2
SELECT *
FROM
(
--column 1 is just for row number, column 2 and 3 for caculation option1, columns 4~6 for caculation option2
SELECT 1 ROW_NUM, NULL time1, NULL qty1 , 2 time2_1 , 2.5 time2_2, 1 qty2 FROM DUAL
UNION
SELECT 2 ROW_NUM, 4.56 time1 , 1 qty1 , NULL time2_1 , NULL time2_2 , NULL qty2 FROM DUAL
)
)SELECT ROW_NUM, coalesce(time1/qty1,(time2_1+time2_2)/qty2) as result FROM DATASET;
db<>fiddle demo
Cheers!!

Order by: Special characters before ABC

I have this sql:
SELECT -1 AS ID, '(None)' AS NAME
FROM TABLE_1 WHERE ID=1
UNION
SELECT ID, NAME
FROM TABLE_2
ORDER BY 2
Table data:
ID | NAME
1 | Direct
2 | Personal
3 | Etc
So if i execute this sql in Oracle 10 it returns these:
Result:
ID | NAME
1 | Direct
3 | Etc
-1 | (None)
2 | Personal
How is it possible to sort the "(None)" always to the top?
If i use
' (None) ' as Name
instead of
'(None)' as Name
It works, because the space before the (None), but that is not a solution.
You can add a dummy column ORDER_COL and then order on that column
select ID, NAME from
(
SELECT -1 AS ID, '(None)' AS NAME, 1 as ORDER_COL FROM TABLE_1 WHERE ID=1
UNION
SELECT ID, NAME, 2 as ORDER_COL FROM TABLE_2
)
order by ORDER_COL, NAME;
Try this. NULLS LAST is the default for ascending order in Oracle. make it NULLS FIRST for '(None)'. Also, use UNION ALL as UNION removes duplicates and is less efficient.
SELECT *
FROM (
SELECT -1 AS ID
,'(None)' AS NAME
FROM TABLE_1
WHERE ID = 1
UNION ALL
SELECT ID
,NAME
FROM TABLE_2
)
ORDER BY CASE
WHEN NAME = '(None)'
THEN NULL
ELSE NAME -- or id if you want
END NULLS FIRST;

how to write a query to count the number of days that a user has logged in?

The site log records a datetime stamp each time a user logs in. I am in need of a query to count the number of days that a user has logged in. Just doing a count on the [DateTime] field doesn't work because a user can login more than once per day. So, I am stumped as how to just count distinct days that they logged in. Basically, we need the query to produce:
UserID
NumberOfDaysLoggedIn
For example, User John (ID 33) logins in as such:
3/26/2008 12:20 PM
3/26/2008 5:58 PM
3/27/2008 11:46 AM
The results we want would be:
UserID NumberofDaysLoggedIn
33 2
Any ideas to produce that results by using oracle query . please suggest any idea
what you should do is round the dates and then put them under distinct.
the function that round dates is trunc().
you can do it like that:
select count(*)
from (select name, trunc(date)
from table
group by name, trunc(date))
where name = 'John';
if you want to get the result for each user you could do it like that:
select name, count(*)
from (select distinct name, trunc(date)
from table)
group by name;
you need to do something like;
select userID, count(distinct trunc(date))
from table
with t as
(
select 1 as id, sysdate as d from dual
union all
select 1 as id, sysdate - 0.5 as d from dual
union all
select 1 as id, sysdate - 0.6 as d from dual
union all
select 1 as id, sysdate - 1 as d from dual
union all
select 2 as id, sysdate - 1 as d from dual
)
select id, count(distinct trunc(d))
from t
group by id
;
You can count distinct dates, something along the lines of:
select count(distinct trunc(t.date))
from table t
where t.userId = some user id
trunc() truncates the date to the day value but can also take format parameter for truncating to a specific unit of measure.

Resources