Oracle date formatting error - ORA-00933: SQL command not properly ended - oracle

I am trying to execute a simple query (ORACLE DB) to display two date columns (START_TIME & END_TIME) along with total counts along with percentage and filtered by a month followed by year the data type defined in the for the two date fields in db as date, i am trying to display the data in the below format
sample data no time stamp
start_time - 01.12.2020
end_time - 02.12.2020
Query
SELECT
to_char(cast(START_TIME as date),'YYYY') as START_BY_YEAR ,
to_char(cast(END_TIME as date),'YYYY') as END_BY_YEAR ,
to_char(cast(START_TIME as date),'MM.YYYY')as START_BY_MONTH,
to_char(cast(END_TIME as date),'MM.YYYY') as END_BY_MONTH,
to_char(cast(START_TIME as date),'DD.MM.YYYY') as START_BY_DAY,
to_char(cast(END_TIME as date),'DD.MM.YYYY') as END_BY_DAY,
COUNT(*) as count,
round(100*ratio_to_report(count(*)) over (), 4) percentage
FROM SCHEMA_NAME.TABLE_NAME
WHERE to_char(cast(START_TIME as date),'MM.YYYY')='12.2020'
and to_char(cast(END_TIME as date),'MM.YYYY')='12.2020'
GROUP BY
START_BY_YEAR,
END_BY_YEAR,
START_BY_MONTH,
END_BY_MONTH,
START_BY_DAY,
END_BY_DAY
order by
START_BY_YEAR desc,
END_BY_YEAR desc,
START_BY_MONTH desc,
END_BY_MONTH desc,
START_BY_DAY desc,
END_BY_DAY desc
I am getting error message
ORA-00904: "END_BY_DAY": invalid identifier
00000 - "%s: invalid identifier"
*Cause:
*Action:
Error at Line: 19 Column: 1
where exactly i am getting error is not clear
expected result set
START_BY_YEAR END_BY_YEAR START_BY_MONTH END_BY_MONTH START_BY_DAY END_BY_DAY
2012 2012 12.2012 12.2012 01.12.2020 02.12.2020
Please note i have expanded the code for easily readable purpose.
Suggestion ?

To solve the error, you have to remove the aliases from the GROUP BY clause.
Also, if your table has date columns, you don't need CASTs and the query can be re-wrtten as:
SELECT
to_char(START_TIME,'YYYY') START_BY_YEAR ,
to_char(END_TIME,'YYYY') END_BY_YEAR ,
to_char(START_TIME,'MM.YYYY') START_BY_MONTH,
to_char(END_TIME,'MM.YYYY') END_BY_MONTH,
to_char(START_TIME,'DD.MM.YYYY') START_BY_DAY,
to_char(END_TIME,'DD.MM.YYYY') END_BY_DAY,
COUNT(*) as count,
round(100*ratio_to_report(count(*)) over (), 4) percentage
FROM TABLE_NAME
WHERE to_char(START_TIME,'MM.YYYY')='12.2020'
and to_char(END_TIME,'MM.YYYY')='12.2020'
GROUP BY
to_char(START_TIME,'YYYY') ,
to_char(END_TIME,'YYYY') ,
to_char(START_TIME,'MM.YYYY') ,
to_char(END_TIME,'MM.YYYY') ,
to_char(START_TIME,'DD.MM.YYYY') ,
to_char(END_TIME,'DD.MM.YYYY')
order by
START_BY_YEAR desc,
END_BY_YEAR desc,
START_BY_MONTH desc,
END_BY_MONTH desc,
START_BY_DAY desc,
END_BY_DAY desc
With a table like this:
create table table_name as
(
select date '2020-12-01' start_time,
date '2020-12-02' end_time
from dual
);
The query gives:
START_BY_YEA END_BY_YEAR START_BY_MON END_BY_MONTH START_BY_DAY END_BY_DAY COUNT PERCENTAGE
------------ ------------ ------------ ------------ ------------ ------------ ----- ----------
2020 2020 12.2020 12.2020 01.12.2020 02.12.2020 1 100
1 row selected.

First check the partial result of:
select cast(END_TIME as date),'DD.MM.YYYY') as END_BY_DAY
FROM SCHEMA_NAME.TABLE_NAME
from the results you can learn about the NLS format of your machin and wrap it with custom to_char format.

Related

Trying to limit the results of a group by query to a range of dates

I'm trying to limit a query's results to the latest 14 distinct PROCESS_DATE dates. To do this, I have used a CTE expression to retrieve the latest and earliest dates for the date range
With these 2 values, I would like to plug them into a group by statement so that I will get results between the two dates
But I am getting this error when I run the query in Oracle
ORA-00904: "MAX_PROCESS_DATE": invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
Error at Line: 24 Column: 60
Line: 24 Column: 60 is the WHERE PROCESS_DATE >= MIN_PROCESS_DATE AND .... MAX_PROCESS_DATE part of the Group By statement
If this is a wrong way to go about this task, please pardon me and suggest a better query. If it's on the right track, how would I fix it so it will run successfully?
WITH cteQUERYRANGE AS
(
SELECT MAX(PROCESS_DATE) AS MAX_PROCESS_DATE, MIN(PROCESS_DATE) AS MIN_PROCESS_DATE FROM
(
SELECT DISTINCT PROCESS_DATE FROM PAYMENTS
ORDER BY PROCESS_DATE DESC
FETCH FIRST 14 ROWS ONLY
)
)
SELECT PROGRAM_CODE AS PROGRAM, BWE_DATE AS "BWE DATE", PROCESS_DATE AS "PROCESSED DATE", GROSS_AMOUNT AS ENTITLEMENTS, FPUC, LWA
FROM PAYMENTS
WHERE PROCESS_DATE >= MIN_PROCESS_DATE AND PROCESS_DATE <= MAX_PROCESS_DATE
GROUP BY PROGRAM_CODE, BWE_DATE, PROCESS_DATE, GROSS_AMOUNT, FPUC, LWA
ORDER BY PROCESS_DATE DESC;
To me, it looks like this (see 3 comments within code):
WITH ctequeryrange AS(SELECT MAX(process_date) AS max_process_date,
MIN(process_date) AS min_process_date
FROM(SELECT DISTINCT process_date
FROM payments
ORDER BY process_date DESC
FETCH FIRST 14 ROWS ONLY)
)
SELECT DISTINCT program_code AS program, --> distinct
bwe_date AS "BWE DATE",
process_date AS "PROCESSED DATE",
gross_amount AS entitlements,
fpuc,
lwa
FROM payments cross join ctequeryrange --> cross join
WHERE process_date >= min_process_date
AND process_date <= max_process_date
ORDER BY process_date DESC; --> no group by
if you want to use columns from a CTE, you have to "reference" it, somehow. As it returns only one row, cross join is safe
as there are no aggregates in your query, no need to GROUP BY - DISTINCT would do
Though, your fetch first 14 rows won't result in 14 rows (if that was your intention) as CTE itself returns only one row.

Oracle query to find records took more than 24hrs to process

I am having a situation were I have to find out such records from the tables who takes more than 24 hrs two load in DW.
so for this I am having two tables
Table 1 :- Which contains the stats about each and every load
Table 2 :- Which contains the stats about when we received the each file to load
Now I want only those records which took more than 24 hrs to load.
The date on which I have received a file is in table 2 whereas when its load is finished in in table 1, so table2 may have more than 1 entries for each file.
I have developed a below query but it's taking more time
SELECT
rcd.file_date,
rcd.recived_on as "Date received On",
rcd.loaded_On "Date Processed On",
to_char(rcd.recived_on,'DY') as "Day",
round((rcd.loaded_On - rcd.recived_on)*24,2) as "time required"
FROM (
SELECT
tbl1.file_date,
(SELECT tbl2.recived_on
FROM ( SELECT recived_on
FROM table2
Where fileName = tbl1.feedName
order by recived_on) tbl2
WHERE rownum = 1) recived_on,
tbl1.loaded_On,
to_char(tbl2.recived_on,'DY'),
round((tbl1.loaded_On - tbl2.recived_on)*24,2)
FROM Table1 tbl1 ,
Table1 tbl2
WHERE
tbl1.id=tbl2.id
AND tbl1.FileState = 'Success'
AND trunc(loaded_On) between '25-Feb-2020' AND '03-Mar-2020'
) rcd
WHERE (rcd.loaded_On - rcd.recived_on)*24 > 24;
I think a lot of your problem most likely stems from the use of the subquery in your column list of your inner query. Maybe try using an analytic function instead. Something like this:
SELECT rcd.file_date,
rcd.recived_on AS "Date received On",
rcd.loaded_On "Date Processed On",
to_char(rcd.recived_on, 'DY') AS "Day",
round((rcd.loaded_On - rcd.recived_on) * 24, 2) AS "time required"
FROM (SELECT tbl1.file_date,
MIN(tbl2.recived_on) OVER (PARTITION BY tbl2.filename) AS recived_on,
tbl1.loaded_On
FROM Table1 tbl1
INNER JOIN Table1 tbl2 ON tbl1.id = tbl2.id
WHERE tbl1.FileState = 'Success'
AND trunc(loaded_On) BETWEEN '25-Feb-2020' AND '03-Mar-2020') rcd
WHERE (rcd.loaded_On - rcd.recived_on) * 24 > 24;
Also, you were selecting some columns in the inner query and not using them, so I removed them.

exclude part of the select not to consider date where clause

i have a select(water readings, previous water reading, other columns) , a "where clause" that is based on date water reading date. however for previous water reading it must not consider the where clause. I want to get previous meter reading regardless where clause date range.
looked at union problem is that i have to use the same clause,
SELECT
WATERREADINGS.name,
WATERREADINGS.date,
LAG( WATERREADINGS.meter_reading,1,NULL) OVER(
PARTITION BY WATERREADINGS.meter_id,WATERREADINGS.register_id
ORDER BY WATERREADINGS.meter_id DESC,WATERREADINGS.register_id
DESC,WATERREADINGS.readingdate ASC,WATERREADINGS.created ASC
) AS prev_water_reading,
FROM WATERREADINGS
WHERE waterreadings.waterreadingdate BETWEEN '24-JUN-19' AND
'24-AUG-19' and isactive = 'Y'
The prev_water_reading value must not be restricted by the date BETWEEN '24-JUN-19' AND '24-AUG-19' predicate but the rest of the sql should be.
You can do this by first finding the previous meter readings for all rows and then filtering those results on the date, e.g.:
WITH meter_readings AS (SELECT waterreadings.name,
waterreadings.date dt,
lag(waterreadings.meter_reading, 1, NULL) OVER (PARTITION BY waterreadings.meter_id, waterreadings.register_id
ORDER BY waterreadings.readingdate ASC, waterreadings.created ASC)
AS prev_water_reading,
FROM waterreadings
WHERE isactive = 'Y')
-- the meter_readings subquery above gets all rows and finds their previous meter reading.
-- the main query below then applies the date restriction to the rows from the meter_readings subquery.
SELECT name,
date,
prev_water_reading,
FROM meter_readings
WHERE dt BETWEEN to_date('24/06/2019', 'dd/mm/yyyy') AND to_date('24/08/2019', 'dd/mm/yyyy');
Perform the LAG in an inner query that is not filtered by dates and then filter by the dates in the outer query:
SELECT name,
"date",
prev_water_reading
FROM (
SELECT name,
"date",
LAG( meter_reading,1,NULL) OVER(
PARTITION BY meter_id, register_id
ORDER BY meter_id DESC, register_id DESC, readingdate ASC, created ASC
) AS prev_water_reading,
waterreadingdate --
FROM WATERREADINGS
WHERE isactive = 'Y'
)
WHERE waterreadingdate BETWEEN DATE '2019-06-24' AND DATE '2019-08-24'
You should also not use strings for dates (that require an implicit cast using the NLS_DATE_FORMAT session parameter, which can be changed by any user in their own session) and use date literals DATE '2019-06-24' or an explicit cast TO_DATE( '24-JUN-19', 'DD-MON-RR' ).
You also do not need to reference the table name for every column when there is only a single table as this clutters up your code and makes it difficult to read and DATE is a keyword so you either need to wrap it in double quotes to use it as a column name (which makes the column name case sensitive) or should use a different name for your column.
I've added a subquery with previous result without filter and then joined it with the main table with filters:
SELECT
WATERREADINGS.name,
WATERREADINGS.date,
w_lag.prev_water_reading
FROM
WATERREADINGS,
(SELECT name, date, LAG( WATERREADINGS.meter_reading,1,NULL) OVER(
PARTITION BY WATERREADINGS.meter_id,WATERREADINGS.register_id
ORDER BY WATERREADINGS.meter_id DESC,WATERREADINGS.register_id
DESC,WATERREADINGS.readingdate ASC,WATERREADINGS.created ASC
) AS prev_water_reading
FROM WATERREADINGS) w_lag
WHERE waterreadings.waterreadingsdate BETWEEN '24-JUN-19' AND '24-AUG-19' and isactive = 'Y'
and WATERREADINGS.name = w_lag.name
and WATERREADINGS.date = w_lag.date

How to replace start date and end date for multiple row data with the maximum and minimum of date in pl/sql

I have a multiple rows data with unique start date and end date combination.
I want to replace the start date with the minimum of all the start dates
and End date with maximum of all the end dates.
Example:
id start Date end Date
1005 09/01/2000 05/31/2001
1005 09/05/2000 05/23/2001
100775 03/15/2005 04/30/2005
100775 03/25/2005 04/22/2005
3273 09/01/2003 12/31/2004
3273 09/11/2003 12/11/2004
Now I want the output to look like:
id start Date end date
1005 09/01/2000 05/31/2001
1005 09/01/2000 05/31/2001
100775 03/15/2005 04/30/2005
100775 03/15/2005 04/30/2005
3273 09/01/2003 12/31/2004
3273 09/01/2003 12/31/2004
I have tried doing this with max and min functions but that doesn't work since I want this to be only one query for using it in oracle report builder.
You can use just use min and max function to get your desired output:
select
id,
min(start_date) OVER (PARTITION BY id) start_date,
max(end_date) OVER (PARTITION BY id) end_date
FROM table1;
SQL Fiddle Demo
Another way of doing this,
UPDATE T1 SET StartDate = MinStartDate, EndDate = MaxEndDate
FROM (
Select id, min(StartDate) as 'MinStartDate',
Max(EndDate) as 'MaxEndDate'
from T1
Group by id
) AS T2
WHERE T1.id = T2.id;

Oracle: extract year from date in listagg

I have a table with date column and would like to obtain a string with distinct years:
ID DATA
1 01/01/2010
2 02/01/2010
3 01/03/2011
4 03/01/2014
5 05/02/2014
From the above table and using listagg I want to get the years
2010
2011
2014
But when I run the following query:
SELECT LISTAGG(EXTRACT(year from data),',')
WITHIN GROUP (ORDER BY data)
FROM (SELECT distinct EXTRACT(year from data)
FROM t_teste)
I get the following error
ORA-00904: "DATA": invalid identifier
Any idea what I'm doing wrong?
You're using a subquery that is not specifying an alias for your extracted year value, and the data column from your table is no longer visible - as it is only in scope inside the subquery.
You can add a column alias and then refer to that directly in the outer query, without needing another extract:
select listagg(anno, ',') within group (order by anno)
from (
select distinct extract(year from data) as anno
from t_teste
);
LISTAGG(ANNO,',')WITHINGROUP(ORDERBYANNO)
-----------------------------------------
2010,2011,2014

Resources