Oracle SQL Group by not working correctly - sql

I am running a query to return a count of a number of completed jobs per week, with the weeks broken down into 4-weekly periods based on a separate financial period table, but the query isn't returning the correct count.
Here is a sample of the code:
select (SELECT (fp.financialperiod || ' week ' ||
ceil(floor((wo.actfinish - p.periodstart+1))/7))
FROM maximo.financialperiods fp
WHERE TRUNC (wo.actfinish) BETWEEN fp.periodstart
AND fp.periodend) fin_period,
wo.wo8 as assetgroup,
Count(wo.wonum)
from maximo.workorder wo
where (TRUNC (wo.actfinish) BETWEEN TO_DATE (:startdate, 'DD/MM/YYYY')
AND TO_DATE (:enddate, 'DD/MM/YYYY'))
group by wo.actfinish,
wo.wo8

I suspect the reason is that you need to aggregate by the first column. Given the structure of your query, this is most easily done using a subquery:
select fin_period, wo.wo8, count(wo.wonum) as cnt
from (select (SELECT (fp.financialperiod || ' week ' || ceil(floor((wo.actfinish - p.periodstart+1))/7))
FROM maximo.financialperiods fp
WHERE TRUNC (wo.actfinish) BETWEEN fp.periodstart AND fp.periodend
) as fin_period,
wo.wo8 as assetgroup, wo.wonum
from maximo.workorder wo
where TRUNC(wo.actfinish) BETWEEN TO_DATE (:startdate, 'DD/MM/YYYY')
AND TO_DATE (:enddate, 'DD/MM/YYYY')
) t
group by fin_period, wo.wo8;

SELECT
(SELECT (fp.financialperiod || ' week ' || ceil(floor((wo.actfinish - p.periodstart+1))/7))
FROM maximo.financialperiods fp
WHERE TRUNC (wo.actfinish) BETWEEN fp.periodstart AND fp.periodend) fin_period, wo.wo8 as assetgroup, Count(wo.wonum) AS [Count]
FROM maximo.workorder wo
WHERE (TRUNC (wo.actfinish) BETWEEN TO_DATE (:startdate, 'DD/MM/YYYY')
AND TO_DATE (:enddate, 'DD/MM/YYYY'))
GROUP BY wo.actfinish, wo.wo8, fp.financialperiod

Related

oracle sql how to get apart year and apart month from varchar

I have varchar column name Calc_code, with for example this varchar '100201', first two characters are Year (2010), third and fourth are month (02) and the last two characters are not related with date so they are not important. How will I convert into date so that I can in select in where clause give something like this:
select *
from table_a
where Calc_code >= p_year_from || p_month_from
and Calc_code <= p_year_to || p_month_to
You need the first 4 chars and you can get them with the function substr():
select *
from table_a
where substr(Calc_code, 1, 4) >= p_year_from || p_month_from
and substr(Calc_code, 1, 4) <= p_year_to || p_month_to
I assume that p_year_from, p_month_from, p_year_to and p_month_to are strings (since you concatenate them), padded with a 0 at the left if necessary.
Need to take the first four digits into consideration and then we can convert it using TO_DATE as following:
select to_date(SUBSTR('100201',1,4),'YYMM') FROM DUAL;
Note that output will be the first date of the month as the date is not given in TO_DATE function
Cheers!!
Use TO_DATE with the first 4 character substring of your value:
SELECT TO_DATE( SUBSTR( calc_code, 1, 4 ), 'RRMM' )
FROM DUAL;
So your assuming your p_year_from, etc values are numbers then the code would be:
SELECT *
FROM table_a
WHERE TO_DATE( SUBSTR( calc_code, 1, 4 ), 'RRMM' )
BETWEEN TO_DATE( TO_CHAR( p_year_from, '00' ) || TO_CHAR( p_month_from, '00' ), 'RRMM' )
AND TO_DATE( TO_CHAR( p_year_to, '00' ) || TO_CHAR( p_month_to, '00' ), 'RRMM' )
with YR_FROM as (select to_char(to_date(SUBSTR('100201',1,4),'YYMM'), 'YYYY-MM') FROM DUAL),--2010 Feb
YR_TO as (select to_char(to_date(SUBSTR('100301',1,4),'YYMM'), 'YYYY-MM') FROM DUAL) --2010 March
Select * from table_a
where to_char(CREATE_DATE, 'YYYY-MM') between (select * from YR_FROM) and (select * from YR_TO);
--Will give zero results
with YR as (select to_char(to_date(SUBSTR('100201',1,4),'YYMM'), 'YYYY-MM') FROM DUAL)
Select * from table_a
where to_char(CREATE_DATE, 'YYYY-MM') >= (select * from YR)
and to_char(CREATE_DATE, 'YYYY-MM') <=(select * from YR);

Causing a invalid day error when using the adhoc dates on the WHERE clause

I am told to create a calendar-like list of dates based on existing records and to tell if a record exists on that date.
To have a sample scenario, take these records from a sample table: TIME_LOG(ID, PUNCH_TIME).
1 1/1/2018 8:00:00
2 1/1/2018 12:12:00
...
n 2/14/2020 8:00:00
In this example, what I need to do is to:
Get all the months present on the TIME_LOG, which are January 2018 and February 2020.
List all dates inside those two months, which are January 1 - 31, 2018 plus February 1 - 29, 2020. And then set it as column DATE_TOKEN of the result set.
Set 'Record found' or 'No records found' whether the value from DATE_TOKEN column exists from TIME_LOG. Make that as column IS_FOUND of the result set.
To retrieve that result set, this is my preliminary query:
SELECT a.date_token,
NVL2 (b.date_token, 'Record found.',
'No records found.') AS is_found
FROM (SELECT TO_DATE (a.MONTH || '/' || b.DAY || '/' || a.YEAR,
'MM/DD/YYYY'
) AS date_token
FROM (SELECT TO_CHAR (EXTRACT (MONTH FROM a.punch_time)
) AS MONTH,
TO_CHAR (EXTRACT (YEAR FROM a.punch_time)) AS YEAR
FROM vw_each_punch a
GROUP BY TO_CHAR (EXTRACT (MONTH FROM a.punch_time)),
TO_CHAR (EXTRACT (YEAR FROM a.punch_time))) a
JOIN
(SELECT TO_CHAR (ROWNUM) AS DAY
FROM DUAL
CONNECT BY ROWNUM <= 31) b
-- I placed this condition to eliminate dates such as February 31, etc.
-- and it works unless I uncomment the WHERE clause below.
ON b.DAY <=
EXTRACT (DAY FROM LAST_DAY (TO_DATE ( a.MONTH
|| '/1/'
|| a.YEAR,
'MM/DD/YYYY'
)
)
)
) a
LEFT JOIN
(SELECT TRUNC (a.punch_time) AS date_token
FROM vw_each_punch a
GROUP BY TRUNC (a.punch_time)) b ON b.date_token = a.date_token
-- WHERE TRUNC (a.date_token, 'MONTH') = '1-FEB-2020'
ORDER BY 1, 2
This works fine and retrieves 60 rows on the resultset, similar to this (DATE_TOKEN, IS_FOUND):
1/1/2018 Record found.
1/2/2018 No records found.
1/3/2018 No records found.
...
2/13/2018 No records found.
2/14/2018 Record found.
2/15/2018 No records found.
...
until I needed to filter the resultset with certain months. When I try to uncomment the WHERE clause on my query to show only the dates on February 2020, it throws me an error:
ORA-01839: date not valid for month specified
The resultset loads completely without any errors as long as there are no WHERE clause, shown by the previous resultset with 60 rows. I suspect the error originates from the fact that February 2020 is less than 31 days, but that has been removed by b.DAY <= EXTRACT (DAY FROM LAST_DAY (TO_DATE (a.MONTH || '/1/' || a.YEAR, 'MM/DD/YYYY'))) condition. Also, I changed the condition from February 2020 to January 2018 but it still doesn't work.
Is there any way I can use the WHERE clause without it throwing an ORA-01839 error?
Here is a running SQL sample using subquery factoring if you need to run it ASAP:
WITH time_log_temp (id, punch_time)
AS (SELECT 1,
To_date('2018-1-1 8:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM')
FROM dual
UNION ALL
SELECT 2,
To_date('2018-1-1 12:12:00 AM', 'YYYY-MM-DD HH:MI:SS AM')
FROM dual
UNION ALL
SELECT 2,
To_date('2020-2-14 8:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM')
FROM dual),
inter
AS (SELECT a.date_token,
Nvl2 (b.date_token, 'Record found.', 'No records found.') AS
is_found
FROM (SELECT To_date (a.month
|| '/'
|| b.day
|| '/'
|| a.year, 'MM/DD/YYYY') AS date_token
FROM (SELECT To_char (Extract (month FROM a.punch_time)) AS
MONTH,
To_char (Extract (year FROM a.punch_time)) AS
YEAR
FROM time_log_temp a
GROUP BY To_char (Extract (month FROM a.punch_time)),
To_char (Extract (year FROM a.punch_time))) a
join (SELECT To_char (ROWNUM) AS DAY
FROM dual
CONNECT BY ROWNUM <= 31) b
-- I placed this condition to eliminate dates such as February 31, etc.
-- and it works unless I uncomment the WHERE clause below.
ON b.day <= Extract (day FROM Last_day (
To_date (a.month
|| '/1/'
|| a.year,
'MM/DD/YYYY')))) a
left join (SELECT Trunc (a.punch_time) AS date_token
FROM time_log_temp a
GROUP BY Trunc (a.punch_time)) b
ON b.date_token = a.date_token
ORDER BY 1,
2)
SELECT *
FROM inter
-- WHERE Trunc(date_token, 'MONTH') = '1-JAN-2018'
I'm not sure how you're getting exactly that error from the filters you've shown, but fundamentally the problem is that you're comparing your (truncated) date with a string, which has to be implicitly converted to a date for the comparison. That relies on your NLS settings, specifically NLS_DATE_FORMAT; and that clearly don't match the string format.
You should change the filter to compare against a date instead:
WHERE Trunc(date_token, 'MONTH') = to_date('1-FEB-2020', 'DD-MON-YYYY')
... which is still relying on your NLS_DATE_LANGUAGE setting for the month name, or slightly better:
WHERE Trunc(date_token, 'MONTH') = to_date('01-02-2020', 'DD-MM-YYYY')
or better still, shorter and unambiguous, use an ANSI date literal:
WHERE Trunc(date_token, 'MONTH') = DATE'2020-02-01'
Another approach that avoids having to truncate the date column (which may prevent an index being used in your real query) is to use a date range instead:
WHERE date_token >= DATE '2020-02-01' AND date_token < DATE'2020-03-01'

Using MM in Oracle [duplicate]

This question already has an answer here:
Using IW and MM in Oracle
(1 answer)
Closed 9 years ago.
I don't know what's wrong with my query. I just put 'MM' in my query to get the monthly result. But when I run it, it gives me a daily result in 365 days, instead of monthly result. Please help me.
Here's my query:
SELECT 'Data'
|| ',' || TO_CHAR(d.dtime_day, 'MM/dd/yyyy')
|| ',' || NVL(o.cnt_opened, 0) --as cnt_opened
|| ',' || NVL(c.cnt_closed, 0) --as cnt_closed
FROM owner_dwh.dc_date d
LEFT JOIN (
SELECT
TRUNC(t.create_time, 'MM') AS report_date,
count(*) AS cnt_opened
FROM app_account.otrs_ticket t
WHERE t.create_time BETWEEN SYSDATE - 365 AND SYSDATE
GROUP BY TRUNC(t.create_time, 'MM')
) o ON d.dtime_day = o.report_date
LEFT JOIN (
SELECT
TRUNC(t.close_time, 'MM') AS report_date,
count(*) AS cnt_closed
FROM app_account.otrs_ticket t
WHERE t.close_time BETWEEN SYSDATE - 365 AND SYSDATE
GROUP BY TRUNC(t.close_time, 'MM')
) c ON d.dtime_day = c.report_date
WHERE d.dtime_day BETWEEN SYSDATE - 365 AND SYSDATE
ORDER BY d.dtime_day;
Result:
Data,01/25/2013,0,0
Data,01/26/2013,0,0
Data,01/27/2013,0,0
Data,01/28/2013,0,0
Data,01/29/2013,0,0
Data,01/30/2013,0,0
Your initial query against DC_DATE is getting every date in the last 365 days. If you ran just that part:
SELECT 'Data'
||','||TO_CHAR(D.DTIME_DAY,'MM/dd/yyyy')
FROM OWNER_DWH.DC_DATE d
WHERE d.DTIME_DAY BETWEEN SYSDATE -365 AND SYSDATE
ORDER BY D.DTIME_DAY;
... you would expect to get 365 rows returned.
The subqueries you are outer-joining to are only going to return a summary count for the first day of each month. So when you join you will get an actual value (which could be zero) on the first of each month, but always zero on every other date. You could avoid that just by adjusting your where clause, e.g.:
WHERE d.DTIME_DAY BETWEEN SYSDATE -365 AND SYSDATE
AND d.DTIME_DAY = TRUNC(s.DTIME_DAY, 'MM')
The that will only show you the first of each month, and the outer joins will still show dates with zero values if there is no matching data from the subqueries.

Automatically updating the timestamp values in User Defined Reports (SQL Developer)

I have defined an User Defined Report in Oracle SQL Developer with the following query to be run monthly:
SELECT TO_CHAR(SUMMARY_TIMESTAMP,'YYYY-MM-DD') AS day, Count(ID) AS row_count
FROM S_SEARCH_SUMMARIES
WHERE SUMMARY_TIMESTAMP > '01-Nov-13' AND SUMMARY_TIMESTAMP < '01-Dec-13'
AND SOURCE_INSTITUTION = 'My Institution'
GROUP BY TO_CHAR(SUMMARY_TIMESTAMP,'YYYY-MM-DD')
ORDER BY day DESC
Is there any way to automatically update the SUMMARY_TIMESTAMP on the basis of the current month, instead of manually changing it?
Maybe there is a cleaner way to do this, but one thought is to add something like this to your query:
WITH dates
AS (SELECT TO_DATE (
EXTRACT (YEAR FROM SYSDATE) || '-' || EXTRACT (MONTH FROM
SYSDATE) || '-01',
'YYYY-MM-DD')
DT_END,
TO_DATE ( (EXTRACT (YEAR FROM ADD_MONTHS (SYSDATE, -1))) ||
'-' || EXTRACT (MONTH FROM ADD_MONTHS (SYSDATE, -1))
|| '-01',
'YY-MM-DD')
DT_START
FROM DUAL)
SELECT TO_CHAR (SUMMARY_TIMESTAMP, 'YYYY-MM-DD') AS day,
COUNT (ID) AS row_count
FROM S_SEARCH_SUMMARIES, dates
WHERE SUMMARY_TIMESTAMP > dates.DT_START
AND SUMMARY_TIMESTAMP < dates.DT_END
AND SOURCE_INSTITUTION = 'My Institution'
GROUP BY TO_CHAR (SUMMARY_TIMESTAMP, 'YYYY-MM-DD')
ORDER BY day DESC

how to pass outer query's table in inner query

This is my whole query
SELECT empmst.emp_id, empmst.emp_name,
(SELECT RTRIM
(XMLAGG (XMLELEMENT (e, d || ',')).EXTRACT ('//text()').EXTRACT
('//text()'),
','
)
FROM (SELECT TO_DATE ('01-04-2012', 'dd-MM-yyyy') - 1
+ ROWNUM AS d
FROM all_objects
WHERE TO_DATE ('01-04-2012', 'dd-MM-yyyy') - 1 + ROWNUM <=
TO_DATE ('30-04-2012', 'dd-MM-yyyy')
MINUS
SELECT tsd.ts_date
FROM ts_dtl tsd
WHERE empmst.emp_id = tsd.emp_id
AND tsd.ts_date BETWEEN TO_DATE ('01-04-2012',
'dd-MM-yyyy'
)
AND TO_DATE ('30-04-2012',
'dd-MM-yyyy'
))) AS day11
FROM emp_mst empmst
WHERE TSD.EMP_ID=EMPMST.EMP_ID
ORDER BY empmst.emp_id
I want to pass EMPMST.EMP_ID of the outer query to the inner query but inner query does'nt get EMPMST.EMP_ID in the where clause.
Please tell me how to pass outer query from table to inner query.
Inner query is as follows
(SELECT RTRIM
(XMLAGG (XMLELEMENT (e, d || ',')).EXTRACT ('//text()').EXTRACT
('//text()'),
','
)
FROM (SELECT TO_DATE ('01-04-2012', 'dd-MM-yyyy') - 1 + ROWNUM AS d
FROM all_objects
WHERE TO_DATE ('01-04-2012', 'dd-MM-yyyy') - 1 + ROWNUM <=
TO_DATE ('30-04-2012', 'dd-MM-yyyy')
MINUS
SELECT tsd.ts_date
FROM emp_mst empmst, ts_dtl tsd
WHERE empmst.emp_id = tsd.emp_id
AND ts_date BETWEEN TO_DATE ('01-04-2012', 'dd-MM-yyyy')
AND TO_DATE ('30-04-2012', 'dd-MM-yyyy')))
I want emp_name and emp_id and TS_date where TS_DATE which are not present in TS_DTL of april means the rest of the days of april which are not there in TS_DTL table
Please find the below which will achieve the same
SELECT EMPMST.EMP_ID,
EMPMST.EMP_NAME,
rtrim(xmlagg(xmlelement(e, b.d1 || ',')).extract('//text()')
.extract('//text()'),
',') as day11
from (SELECT TO_DATE('01-04-2012', 'dd-MM-yyyy') - 1 + rownum AS d1
FROM all_objects
WHERE TO_DATE('01-04-2012', 'dd-MM-yyyy') - 1 + rownum <=
TO_DATE('30-04-2012', 'dd-MM-yyyy')) b,
EMP_MST EMPMST
WHERE EMPMST.EMAIL_ID IS NOT NULL
AND EMPMST.DEPT IN ('Technical')
AND EMPMST.EMP_STATUS_LKP_ID = 201
AND b.d1 NOT IN
(SELECT TSD.TS_DATE as d2
FROM TS_DTL TSD
WHERE TSD.TS_DATE BETWEEN TO_DATE('01-04-2012', 'dd-MM-yyyy') AND
TO_DATE('30-04-2012', 'dd-MM-yyyy')
AND TSD.EMP_ID = EMPMST.EMP_ID)
ORDER BY EMPMST.EMP_ID