oracle invalid format model with comparing dates - oracle

I am trying to run the following query. I am passing the date from a parameter
SELECT to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP
,sum(TwoG) TwoG
,sum(ThreeG) ThreeG
,sum(FourG) FourG
FROM (
SELECT *
FROM T_CA_Cellsdown
UNION ALL
SELECT *
FROM T_CA_Cellsdown_TFVF
)
WHERE trunc(TIMESTAMP) = trunc(to_date('03-Apr-16', 'dd-Mon-yy'))
AND Type = 'Network'
GROUP BY to_char(TIMESTAMP, 'HH24') * 1
ORDER BY to_char(TIMESTAMP, 'HH24') * 1
Error in query: ORA-01481: invalid number format model
The following query works. I am just trying to change sysdate to a date string which I pass as param through user input in php. Why it is being multiplied by 1 I do not know, it is a query that works and provides the required result.
SELECT to_char(TIMESTAMP, 'HH24') * 1
,sum(TwoG) TwoG
,sum(ThreeG) ThreeG
,sum(FourG) FourG
FROM (
SELECT *
FROM T_CA_Cellsdown
UNION ALL
SELECT *
FROM T_CA_Cellsdown_TFVF
)
WHERE trunc(TIMESTAMP) = trunc(sysdate-1)
AND Type = 'Network'
GROUP BY to_char(TIMESTAMP, 'HH24') * 1
ORDER BY to_char(TIMESTAMP, 'HH24') * 1

The problem is the last line:
ORDER BY to_char(TIMESTAMP, 'HH24') * 1
In the order-by clause it's seeing TIMESTAMP as the column alias from the select list, not as the original TIMESTAMP from the table; so it contains a number, not a timestamp.
So if you had a timestamp where the hours value was 13, say, you're doing
ORDER BY to_char(13, 'HH24') * 1`
... and as the first argument is a number you're using the numeric version of TO_CHAR(), and HH24 isn't a valid format model for that. Which is what the error message says.
You can just do:
ORDER BY TIMESTAMP;
To use your original pattern you'd need to supply a table alias for your inline view, and then use that alias in the order-by, to qualify the TIMESTAMP. By default it's using the column alias version.

order by is executed at last. By the time order by is executed, you are deriving an new column in select statement as timestamp. To overcome it, just use timestamp in order by
SELECT to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP
,sum(TwoG) TwoG
,sum(ThreeG) ThreeG
,sum(FourG) FourG
FROM (
SELECT *
FROM T_CA_Cellsdown
UNION ALL
SELECT *
FROM T_CA_Cellsdown_TFVF
)
WHERE trunc(TIMESTAMP) = trunc(to_date('03-APR-16', 'dd-Mon-yy'))
AND Type = 'Network'
GROUP BY to_char(TIMESTAMP, 'HH24') * 1
ORDER BY TIMESTAMP -- TIMESTAMP here refers to derived column at line 1
Or use this
SELECT to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP1 -- Or use different alias
,sum(TwoG) TwoG
,sum(ThreeG) ThreeG
,sum(FourG) FourG
FROM (
SELECT *
FROM T_CA_Cellsdown
UNION ALL
SELECT *
FROM T_CA_Cellsdown_TFVF
)
WHERE trunc(TIMESTAMP) = trunc(to_date('03-APR-16', 'dd-Mon-yy'))
AND Type = 'Network'
GROUP BY to_char(TIMESTAMP, 'HH24') * 1
ORDER BY to_char(TIMESTAMP, 'HH24') * 1
I replicated the issue here. - This will not work.
select to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP,sum(col1) from
(
select sysdate-1 as TIMESTAMP,1 as col1 from dual
union all
select sysdate-1 as TIMESTAMP,2 as col1 from dual
)
where trunc(sysdate)=trunc(to_date('04-APR-16','Dd-Mon-YY'))
group by to_char(TIMESTAMP, 'HH24') * 1
order by to_char(TIMESTAMP, 'HH24') * 1
But This statement will work.
select to_char(TIMESTAMP, 'HH24') * 1 TIMESTAMP1,sum(col1) from (
select sysdate-1 as TIMESTAMP,1 as col1 from dual
union all
select sysdate-1 as TIMESTAMP,2 as col1 from dual
)
where trunc(sysdate)=trunc(to_date('04-APR-16','Dd-Mon-YY'))
group by to_char(TIMESTAMP, 'HH24') * 1
order by to_char(TIMESTAMP, 'HH24') * 1 --As TIMESTAMP is now reflecting the column from inner table.

Related

Sum totals of timestamp

I have the following query to calculate the summation timestamp
SELECT SUM(TIME_SPENT) FROM
(
select a - b as time_spent from tbl1 where name = 'xxx'
union all
select c - d as time_spent from tbl2 where name= 'yyy'
)a;
The sub-query return result as
+00 00:01:54.252000
But the entire query return error as ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND.
Understand it required something like this
SELECT COALESCE (
(to_timestamp('2014-09-22 16:00:00','yyyy/mm/dd HH24:MI:SS') - to_timestamp('2014-09-22 09:00:00','yyyy/mm/dd HH24:MI:SS')) -
(to_timestamp('2014-09-22 16:00:00','yyyy/mm/dd HH24:MI:SS') - to_timestamp('2014-09-22 09:00:00','yyyy/mm/dd HH24:MI:SS')), INTERVAL '0' DAY) FROM DUAL;
How can achieve along with sub-queries that retrieve data from Timestamp type columns?
You cannot sum INTERVAL DAY TO SECOND in Oracle. I think this is one of the top rated open feature request.
You can cast the TIMESTAMP into DATE values, then the result is the difference in days. Multiply by 24*60*60 is you like to get seconds instead:
SELECT SUM(TIME_SPENT) * 24*60*60 FROM FROM
(
select CAST(a AS DATE) - CAST(b AS DATE) as time_spent from tbl1 where name = 'xxx'
union all
select CAST(d AS DATE) - CAST(d AS DATE) as time_spent from tbl2 where name= 'yyy'
);
Or you can write a function which converts INTERVAL DAY TO SECOND into seconds:
CREATE OR REPLACE FUNCTION GetSeconds(ds INTERVAL DAY TO SECOND) DETERMINISTIC RETURN NUMBER AS
BEGIN
RETURN EXTRACT(DAY FROM ds)*24*60*60
+ EXTRACT(HOUR FROM ds)*60*60
+ EXTRACT(MINUTE FROM ds)*60
+ EXTRACT(SECOND FROM ds);
END;
and use it like this:
SELECT SUM(TIME_SPENT), numtodsinterval(SUM(TIME_SPENT), 'second')
(
select GetSeconds(a-b) as time_spent from tbl1 where name = 'xxx'
union all
select GetSeconds(c-d) as time_spent from tbl2 where name= 'yyy'
);
try using below query
SELECT sum(extract(second from time_spent)) FROM
(
select a - b as time_spent from test2 where name = 'xxx'
union all
select c - d as time_spent from tbl2 where name= 'yyy'
)a;
Looks like time_spent column is timestamp type in your table an d it is not able to pass the correct data type in Sum function. Use extract function to get Seconds from time_spent.
with t(a,b) as (
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-23 16:00:00.001' from dual union all
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-24 16:00:00.001' from dual union all
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-25 16:00:00.001' from dual union all
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-26 16:00:00.001' from dual union all
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-27 16:00:00.001' from dual
)
select
sum( (date'1-1-1'+(b-a)*24*60*60 - date'1-1-1')) as ssum_seconds_1,
round(sum( (date'1-1-1'+(b-a)*24*60*60 - date'1-1-1')), 3) as ssum_seconds_rounded,
numtodsinterval( round(sum( (date'1-1-1'+(b-a)*24*60*60 - date'1-1-1')), 3), 'second') dsint
from t
/

How to convert this code from oracle to redshift?

I am trying to implement the same in redshift and i am finding it little difficult to do that. Since redshift is in top of postgresql engine, if any one can do it in postgresql it would be really helpfull. Basically the code gets the count for previous two month at column level. If there is no count for exact previous month then it gives 0.
This is my code:
with abc(dateval,cnt) as(
select 201908, 100 from dual union
select 201907, 200 from dual union
select 201906, 300 from dual union
select 201904, 600 from dual)
select dateval, cnt,
last_value(cnt) over (order by dateval
range between interval '1' month preceding
and interval '1' month preceding ) m1,
last_value(cnt) over (order by dateval
range between interval '2' month preceding
and interval '2' month preceding ) m2
from (select to_date(dateval, 'yyyymm') dateval, cnt from abc)
I get error in over by clause. I tried to give cast('1 month' as interval) but still its failing. Can someone please help me with this windows function.
expected output:
Regards
This is how I would do it. In Redshift there's no easy way to generate sequences, do I select row_number() from an arbitrary table to create a sequence:
with abc(dateval,cnt) as(
select 201908, 100 union
select 201907, 200 union
select 201906, 300 union
select 201904, 600),
cal(date) as (
select
add_months(
'20190101'::date,
row_number() over () - 1
) as date
from <an arbitrary table to generate a sequence of rows> limit 10
),
with_lag as (
select
dateval,
cnt,
lag(cnt, 1) over (order by date) as m1,
lag(cnt, 2) over (order by date) as m2
from abc right join cal on to_date(dateval, 'YYYYMM') = date
)
select * from with_lag
where dateval is not null
order by dateval

calculate the running total over the column contain date difference in HH:MI:SS format in oracle

I have to find the running total over the column interval.
SELECT
( ( EXTRACT(DAY FROM intrvl) * 24 ) + ( EXTRACT(HOUR FROM intrvl) ) ) ||':'||
EXTRACT(MINUTE FROM intrvl) ||':'||
EXTRACT(SECOND FROM intrvl) ||':'|| as interval
FROM
(
SELECT
( to_timestamp(TO_CHAR(date_column_name,'dd-mon-rrrr hh:mi:ss') ) ) - ( to_timestamp(TO_CHAR(date_column_name,'dd-mon-rrrr hh:mi:ss') ) ) intrvl
FROM
dual
);
currrently Interval column of table has below data:
Interval(HH:mi:ss)
0:4:23
696:1:36
696:4:51
8760:1:18
The best I can come up with is this. Note that the interval data type does not take a format model for displaying - you can't force an interval of 25 hours to be displayed as 25:00:00 (although you can use that to INPUT an interval). Instead, it will be shown as 01 01:00:00 (meaning, a day and an hour).
with
tbl (interv) as (
select interval '0:4:23' hour(9) to second from dual union all
select interval '696:1:36' hour(9) to second from dual union all
select interval '696:4:51' hour(9) to second from dual union all
select interval '8760:1:18' hour(9) to second from dual
)
select interval '1' day * sum(date '2000-01-01' + interv - date'2000-01-01')
as sum_interv
from tbl;
SUM_INTERV
--------------------
+423 00:12:08.000000
In your original attempt you were trying to get a STRING output. I am not sure that's wise, but if that's what you need you can do it like so:
with
tbl (interv) as (
select interval '0:4:23' hour(9) to second from dual union all
select interval '696:1:36' hour(9) to second from dual union all
select interval '696:4:51' hour(9) to second from dual union all
select interval '8760:1:18' hour(9) to second from dual
)
, prep (sum_interv) as (
select interval '1' day * sum(date '2000-01-01' + interv - date'2000-01-01')
from tbl
)
select to_char( extract(day from sum_interv) * 24
+ extract(hour from sum_interv), 'fm999999999' ) || ':' ||
to_char( extract(minute from sum_interv), 'fm00' ) || ':' ||
to_char( extract(second from sum_interv), 'fm00' ) as sum_interv
from prep
;
SUM_INTERV
------------------
10152:12:08

How can i avoid rownum as a column when limiting records in oracle?

when i run the query:
select *
from ( select a.*,
ROWNUM rnum
from ( select *
from test
order by null ) a
where ROWNUM <= 2000 )
where rnum >=1
I'm getting all the columns along with rownum as a separate column which I don't want, How to achieve this or is there any way to limit records?
Since the final filter is for ROWNUM >= 1 (which will always be true), you can eliminate that and just use:
select *
from (
select *
from test
order by null
)
where ROWNUM <= 2000
(Note: ORDER BY NULL will apply a non-deterministic ordering.)
If you want to specify a different starting row then you will need to specify the columns you want to return:
select col_1,
col_2,
-- ...
col_n
from (
select a.*,
ROWNUM rnum
from (
select *
from test
order by null
) a
where ROWNUM <= 2000
)
WHERE rnum > 1000
In Oracle 12c you can use:
SELECT *
FROM test
ORDER BY NULL
FETCH FIRST 2000 ROWS ONLY;
or
SELECT *
FROM test
ORDER BY NULL
OFFSET 1000 ROWS FETCH NEXT 1000 ROWS ONLY;

Oracle query with rownum and orderBy return wrong records

I got an confusing result when working with rownum of Oracle10g.
first query
select * from A where name like '%test' order by name asc
==> return 1 record
second query with rownum
select * from (
select * from A where name like '%test'
order by name asc
)
where rownum <= 2
==> return 2 records
If I remove 'order by' then it will return 1 record.
Any one can help me to explain this behavior?
maybe you wanted this functionality:
select * from (
select X.*, rownum r from (
select * from A where name like '%test'
order by name asc
) X
)
where r <= 2
or alternatively
select * from (
select A.*, ROW_NUMBER() OVER (ORDER BY name) r
from A where name like '%test'
)
where r <= 2

Resources