Hi I need a formula that will give me the difference in working days (M,T,W,Th) between two dates. So in other words just excluding weekends.
My current formula is: ROUND({systemnotes.date}-{trandate})
This works great except for that it counts Saturday and Sunday.
Thanks for the help!
Here is a formula that will get you the difference between two dates excluding Saturday and Sunday as a number of days. Put this into a formula(numeric) field in your search.
ROUND(((TO_CHAR({systemnotes.date}, 'J') - TO_CHAR({trandate}, 'J'))) + MOD(({systemnotes.date} - {trandate}), 1) - ((((TRUNC({systemnotes.date}, 'D')) - (TRUNC({trandate}, 'D')))/7)*2) - (CASE WHEN TO_CHAR({trandate}, 'DY') = 'SUN' THEN 1 ELSE 0 END) - (CASE WHEN TO_CHAR({systemnotes.date}, 'DY') = 'SAT' THEN 1 ELSE 0 END), 2)
Sorry, it's a bit of a long one, but in the formula {systemnotes.date} is your start date and {trandate} is your end date.
Related
I need to handle specific scenario in StorProc where I need to do date calculation excluding Sat & Sun. Weekends are holiday I need to handle the data within working days.
I have implemented below code
if (purchase_date = (trunc(sysdate)-2) or purchase_date = (trunc(sysdate)-1)) Then
specific operation
As I have to exclude Sat & Sun by above implementation is giving wrong results obliviously . For example if today is Monday it has to give me back the date of Friday, my implementation is giving me Saturday or Sunday. I need to calculation with dates for weekdays only. Any help would be appreciated.
Thanks
To compare it to the previous week day, you can use:
IF purchase_date = TRUNC(SYSDATE)
- CASE TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')
WHEN 0 THEN 3
WHEN 6 THEN 2
ELSE 1
END
THEN
-- Do something
NULL;
END IF;
TRUNC(date_value) - TRUNC(date_value, 'IW') will count the number of days since the start of the ISO week (which is always midnight on Monday).
Note: Do not use TO_CHAR(date_value, 'D') in an international setting as it will give a different result depending on which country you run it in (the week starts on Friday in Bangladesh, Saturday in some Middle-Eastern countries, Sunday in America and Monday in most of Europe).
I have the following query that gets the week of a date:
SELECT pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww') semana,
SUM (rta.kms_acumulados) kms,
COUNT
(DISTINCT (CASE
WHEN v.secuencia BETWEEN rta.sec_origen AND rta.sec_destino
THEN v.cod_inc
ELSE '0'
END
)
)
- 1 numincidencias
FROM (SELECT ms.tren, ms.fecha_origen_tren, ms.secuencia, ri.cod_inc
FROM r_incidencias ri, mer_sitra ms
WHERE ri.cod_serv = ms.tren
AND ri.fecha_origen_tren = ms.fecha_origen_tren
AND ri.cod_tipoin IN (SELECT cod_tipo_iincidencia
FROM v_tipos_incidencias
WHERE grupo = '45')
AND ri.punto_desde = ms.cod_estacion) v,
r_trenes_asignar rta,
r_maquinas rm,
planificador.pl_dh_material pdm
WHERE rta.fecha BETWEEN TO_DATE ('21/09/2018', 'dd/mm/yyyy') AND TO_DATE ('21/09/2018',
'dd/mm/yyyy'
)
AND rta.serie >= 4000
AND rta.matricula_ant IS NOT NULL
AND rm.matricula_maq = rta.matricula_ant
AND rm.cod_serie = pdm.id_material
AND rta.grafico BETWEEN pdm.desde AND pdm.hasta
AND v.tren(+) = rta.tren
AND v.fecha_origen_tren(+) = rta.fecha
GROUP BY pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww')
ORDER BY pdm.serie, rta.matricula_ant, TO_CHAR (fecha, 'ww')
For example week 1
I want to display
week 1 : 1 january - 7 january
How can I get this?
Oracle offers the TRUNC(datestamp, format) function to manipulate dates this way. You may use a variety of format strings to get the first day of a quarter, year, or even the top of the hour.
Given a particular datestamp value, Oracle returns midnight on the first day of the present week with this expression:
TRUNC(datestamp,'DY')
You can add days to a datestamp. Therefore this expression gives you midnight on the last day of the week
TRUNC(datestamp,'DY') + 6
A WHERE-clause selector for all rows in the present week might be this.
WHERE datestamp >= TRUNC(SYSDATE,'DY')
AND datestamp < TRUNC(SYSDATE,'DY') + 7
Notice that the end of the range is just before (<) midnight on the first day of the next week. You need that because you may have datestamps after midnight on the last day of the week. (Beware using BETWEEN for datestamp ranges.)
And,
SELECT TO_CHAR(TRUNC(SYSDATE,'DY'),'YYYY-MM-DD'),
TO_CHAR(TRUNC(SYSDATE,'DY')+6,'YYYY-MM-DD')
FROM DUAL;
displays the first and last dates of the present week in ISO-like format.
Date arithmetic is cool. It's worth your trouble to study the date-arithmetic functions in your DBMS at least once a year.
I have used the below query to find the number of weeks between two dates:
select count(week_id)
from fw
where week_begin_date >= '2015-01-01'
and week_end_date <= '2015-12-31';
Expected result should be 53 but the actual result is 51.
Kindly help on this.
Can't you just use the week of year function? subtract if needed...
select to_char(to_date('12/31/2015','MM/DD/YYYY'),'WW') from dual;
select To_Number(to_char(to_date('12/31/2015','MM/DD/YYYY'),'WW')) -
To_number(to_char(to_date('01/01/2015','MM/DD/YYYY'),'WW')) +1
from dual;
We have to add +1 because weeks start at 1 not 0.
Now maybe you're after the ISO week format which would be IW instead of WW.
WW: Week of year (1-53) where week 1 starts on the first day of the year and continues to the seventh day of the year.
IW: Week of year (1-52 or 1-53) based on the ISO standard.
I know this is a very old thread, but I used a modified version of this code today that I thought might be beneficial for someone else. My modification solves for the fractional week issue and the removal of the minus sign:
SELECT
CEIL(
ABS(
(
TO_DATE('20160101','YYYYMMDD')
- TO_DATE('20161231','YYYYMMDD')
) / 7
)
) AS DT
FROM DUAL
The ABS function takes the absolute value of the result of subtracting the two dates, thereby eliminating the minus sign if it exists (I switched the order of the dates to demonstrate this). The CEIL function rounds up any fractional week to the next whole week (I changed the year to 2016 to demonstrate this - CEIL is logically equivalent to the ROUNDUP function in Excel). NOTE: We have to apply the ABS function first (inner parenthesis) because CEIL will also round up negative numbers, which would in effect round the weeks down if ABS were applied after CEIL. The result of this calculation is 53 (subtraction of the dates returns about -52.142857, ABS removes the minus sign, CEIL rounds up to 53).
I hope this ends up being useful to someone. Thanks.
Did you try this:
SELECT
REPLACE((
to_date('20151231','yyyymmdd') - to_date('20150101','yyyymmdd')
)/7, '-', '')
FROM
DUAL
CREATE OR REPLACE FUNCTION exlude_weekends (p_date_start DATE,
p_date_end DATE)
RETURN NUMBER
AS
l_no_of_days NUMBER := NULL;
BEGIN
SELECT COUNT ( * ) INTO l_no_of_days
FROM (SELECT date_extraction, TO_CHAR (date_extraction, 'DAY')
FROM (SELECT TO_DATE(p_date_start,'DD-MON-RRRR')
+ LEVEL - 1 date_extraction FROM DUAL CONNECT BY LEVEL <
(TO_DATE (p_date_end, 'DD-MON-RRRR')- TO_DATE (p_date_start,'DD-MON-RRRR'))+ 2)
WHERE TRIM (TO_CHAR (date_extraction, 'DAY')) NOT IN ('SATURDAY', 'SUNDAY'));
RETURN l_no_of_days;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END exlude_weekends;
As you mention in the comments, the key of the function is the hierarchical sub-query:
SELECT TO_DATE(p_date_start,'DD-MON-RRRR') + LEVEL - 1 date_extraction
FROM DUAL
CONNECT BY LEVEL <
(TO_DATE(p_date_end,'DD-MON-RRRR')-
TO_DATE(p_date_start,'DD-MON-RRRR'))+ 2
Hierarchical queries try to traverse a tree (CONNECT BY clause specifies how parents and children are related). In this example we find a tricky use (or abuse) of the connect by.
This sub-query generates date from p_date_start to p_date_end (both inclusive). How it does it?
Note that the expression being compare with LEVEL in the CONNECT BY is a constant, and it is the number of days between start and the day after the end date (why the day after the end date? because it is using < and the day after the end date is the first day out of the interval):
(TO_DATE(p_date_end,'DD-MON-RRRR')-TO_DATE(p_date_start,'DD-MON-RRRR'))+2
The select get the DUAL row (it has only one row) this row has LEVEL 1 (hierarchical query use the pseudo-column LEVEL to indicate the depth from the root where it started to evaluate).
The CONNECT BY checks that this level (1) is in the range of days to be generated.
Evaluates the expression:
TO_DATE(p_date_start,'DD-MON-RRRR') + LEVEL - 1
This is the start date plus the level minus one: this is, the start date.
Now a new cycle in hierarchical evaluation starts: the row generated in the previous cycle (the start date) is evaluated again (the new row will have level 2).
If it is in the range of days to be generated (controlled by the CONNECT BY clause) a new date is generated (the day after the start date).
A new cycle start (level 3)....
And the process iterates until LEVEL is greater than the number of days to be generated (which is the same than the number of levels required to iterate from the start date to the end date).
The outer queries in the function only filter SATURDAYS and SUNDAYS and count the remaining days.
Although oracle is very efficient evaluating this query, this function uses a brute force solution.
A more elegant and mathematical solution can be used (with no iterations). We have an equation that computes the number of a particular day of week between two dates:
TRUNC(( END – START – DAYOFWEEK(END-DAYOFWEEKTOBECOUNTED) + 8) / 7)
where DAYOFWEEK is a function that returns 0-6 (0 Sunday, 1 Monday ... 6 Saturday). And DAYOFWEEKTOBECOUNTED is the number of the day to be counted in the same format.
Note that TO_CHAR(date, 'd') returns the day of week in 1..7 format we must rectify to 0..6 format (In my region monday is the first day of week, so i get sunday as 0 and saturday as 6 with the mod function as follows):
MOD(TO_NUMBER(TO_CHAR(p_date_end, 'd')), 7)
Finally we want the number of days in the interval minus the number of sundays (day 0) and saturdays (day 6). So the final procedure with the mathematical approach will be:
CREATE OR REPLACE FUNCTION exlude_weekends (p_date_start DATE,
p_date_end DATE)
RETURN NUMBER
AS
l_no_of_days NUMBER := NULL;
BEGIN
SELECT TRUNC(p_date_end - p_date_start) + 1 -
( TRUNC((p_date_end - p_date_start -
MOD(to_number(to_char(p_date_end - 0, 'd')), 7)+8)/7)
+ TRUNC((p_date_end - p_date_start -
MOD(to_number(to_char(p_date_end - 6, 'd')), 7)+8)/7)
)
INTO l_no_of_days
FROM DUAL;
RETURN l_no_of_days;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END exlude_weekends;
I have below query but instead of to be putting range of week like '22-OCT-2012 AND 28-OCT-2012' I woulk to put a code like CurrentWeek -2 or CurrentWeek-1, that will avoid to edit the query every week that I need to run it.
Do you know how make this?
tHANKS
LD
SELECT WO.USER_6 AS STYLE
,SUM (CASE WHEN (OPERATION.STATUS ='C' AND OPERATION.CLOSE_DATE BETWEEN '22-OCT-2012' AND '28-OCT-2012') THEN OPERATION.RUN_HRS ELSE 0 END) WEEK43
,SUM (CASE WHEN (OPERATION.STATUS ='C' AND OPERATION.CLOSE_DATE BETWEEN '29-OCT-2012' AND '04-NOV-2012') THEN OPERATION.RUN_HRS ELSE 0 END) WEEK44
FROM WORK_ORDER WO, OPERATION
WHERE WO.BASE_ID = OPERATION.WORKORDER_BASE_ID
AND WO.Lot_ID = Operation.Workorder_Lot_ID
AND WO.Sub_ID = Operation.Workorder_Sub_ID
AND WO.Split_ID = Operation.Workorder_Split_ID
AND WO.TYPE ='W'
AND WO.WAREHOUSE_ID ='MEX-04'
AND OPERATION.CLOSE_DATE BETWEEN '22-OCT-2012' AND '04-NOV-2012'
AND OPERATION.RESOURCE_ID IN ('171-4','171-ADD','171-3' ,'BAMEX-SEWCONC','BAMEX-SEWPATC')
AND OPERATION.RUN > 0
GROUP BY
WO.USER_6
If I understood your question then u can use this to pass the current week days like this
SUM (CASE WHEN (OPERATION.STATUS ='C' AND OPERATION.CLOSE_DATE BETWEEN to_char(trunc(sysdate),'DD-MON-YYYY') and to_char(trunc(sysdate)-6,'DD-MON-YYYY')) THEN OPERATION.RUN_HRS ELSE 0 END) WEEK43
Hope this help u
In this case I will use trunc function:
currentweek will be trunc(sysdate,'D')
current_week - 1 will be trunc(sysdate,'D') - 7
current week - 2 will be trunc(sysdate,'D') - 2 * 7
Attention this will give first day of week sunday. If you want monday you should and one day:
current week - 2 will be trunc(sysdate,'D') - 2 * 7 + 1
UPDATE:
Frank is right, the behavior on first day of week depends on NLS_TERITORY
alter session set NLS_TERRITORY ='UNITED KINGDOM';
select trunc(sysdate,'D') from dual;
05-11-2012
alter session set NLS_TERRITORY ='AMERICA';
select trunc(sysdate,'D') from dual;
04-11-2012