I am trying to make a query in which for every weekday, it picks always the last business day, except for Mondays. On Mondays, it should always pick the last Friday.
To achieve this, it could be either done by selecting or creating an additional column that allows to identify these cases.
Examples:
If first_date = Tuesday then pick Monday
If first_date = Thursday then pick Wednesday
If first_date = Monday then pick Friday
Here my query:
SELECT name,
first_date,
last_day,
product,
TO_DATE(first_date) - TO_DATE(last_day) AS d,
CASE WHEN TO_CHAR(first_date, 'D') = '7' THEN -2
WHEN TO_CHAR(first_date, 'D') = '6' THEN -1
ELSE 0
END
FROM t1.mydata
WHERE d > 50
Any ideas on how to do this best?
You can use conditional with TO_CHAR conversion along with Dy (or Day) argument rather than using those integer representations, those might fail for some regions, for weekdays such that
SELECT CASE WHEN TO_CHAR(your_datecol,'Dy','NLS_DATE_LANGUAGE=English') ='Mon' THEN
'Fri'
WHEN TO_CHAR(your_datecol,'Dy','NLS_DATE_LANGUAGE=English') = 'Sun' THEN
Null
ELSE
TO_CHAR(your_datecol-1,'Dy','NLS_DATE_LANGUAGE=English')
END AS last_business_day
FROM your_table
ignoring the local offical vacancies.
You can use TRUNC(first_date) - TRUNC(fisrt_date, 'IW') to compare the date (truncated to midnight) to the start of the ISO week (which is always midnight on Monday). This will work regardless of the NLS_DATE_LANGUAGE or NLS_TERRITORY settings.
SELECT name,
first_date,
last_day,
product,
last_day - first_date AS d,
first_date
- CASE TRUNC(first_date) - TRUNC(fisrt_date, 'IW')
WHEN 0 THEN -3 -- Monday
WHEN 6 THEN -2 -- Sunday
ELSE -1 -- Other days
END AS prev_business_day
FROM t1.mydata
WHERE last_day - first_date > 50
Note: If you use TO_CHAR(first_date, 'D') then if the day is Monday then it will return: 1 in most of Europe; 2 in America; 3 in some Middle-Eastern countries; and 4 in Bangladesh as they all consider the first day of the week to be a different day db<>fiddle. This is based on the NLS_TERRITORY setting and is not something that can be overridden in the query.
Note: If you use TO_CHAR(first_date, 'Dy') then it will use the NLS_DATE_LANGUAGE session parameter to set the date language so your query would only work in one language. You would need to specify a third argument (i.e. TO_CHAR(first_date, 'Dy', 'NLS_DATE_LANGUAGE=English')) for it to work consistently in an International setting.
Related
I am trying to come up with solution to generate automatic OBIEE 12 report generating automatically for previous business date (e.g. when generating report on Monday, would like to filter data from Friday). I came up with two solutions:
CURRENT_DATE-1
Which obviously works well only for reports generated on Tuesday - Saturday.
I came up also with a more sophisticated solution, which returns no results (do not know why):
CASE
WHEN DayOfWeek(TimestampAdd (SQL_TSI_DAY, -1 , CURRENT_DATE)) = 1
THEN TimestampAdd (SQL_TSI_DAY, -3 , CURRENT_DATE)
WHEN DayOfWeek(TimestampAdd (SQL_TSI_DAY, -1 , CURRENT_DATE)) = 7
THEN TimestampAdd (SQL_TSI_DAY, -2 , CURRENT_DATE)
ELSE TimestampAdd (SQL_TSI_DAY, -1 , CURRENT_DATE)
END
When using this expression for filtering date, OBIEE returns info that no data is matched. For the same date proper results are generated with CURRENT_DATE-1 (ofc besides reports generated on Mondays, Saturdays and Sundays)
This might work for you:
case trim(to_char(sysdate,'DAY'))
when 'MONDAY' then sysdate-3
when 'SUNDAY' then sysdate-2
else sysdate-1
end
Returns the date of previous business day before now.
Bobby
I have spent days trying to figure this out to no avail, so hopefully someone can help me. I have a queried date set which contains several fields including a column of dates. What I want to do is create a new field in my query that tells what the Monday and Friday is for the week of that row's particular date.
So for example; if the date in one of my rows is "1/16/18",
the new field should indicate "1/15/18 - 1/19/18".
So basically I need to be able to extract the Monday date (1/15/18) and the Friday date (1/19/18) of the week of 1/16/18 and then concatenate the two with a dash ( - ) in between. I need to do this for every row.
How on earth do I do this? I've been struggling just to figure out how to find the Monday or Friday of the given date...
Assuming that your column is of type date, you can use trunc to get the first day of the week (monday) and then add 4 days to get the friday.
For example:
with yourTable(d) as (select sysdate from dual)
select trunc(d, 'iw'), trunc(d, 'iw') + 4
from yourTable
To format the date as a string in the needed format, you can use to_char; for example:
with yourTable(d) as (select sysdate from dual)
select to_char(trunc(d, 'iw'), 'dd/mm/yy') ||'-'|| to_char(trunc(d, 'iw') + 4, 'dd/mm/yy')
from yourTable
gives
15/01/2018-19/01/18
There may be a simpler, canonical Oracle method to this but you can still reduce it to a simple calculation on your own either way. I'm going to assume you're dealing with only dates falling Monday through Friday. If you do need to deal with weekend dates then you might have to be more explicit about which logical week they should be attached to.
<date> - (to_char(<date>, 'D') - 2) -- Monday
<date> + (6 - to_char(<date>, 'D')) -- Friday
In principle all you need to do is add/subtract the appropriate number of days based on the current day of week (from 1 - 7). There are some implicit casts going on in there and it would probably be wise to handle those better. You might also want to check into NLS settings to make sure you can rely on to_char() using Sunday as the first day of week.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm
You can also use the NEXT_DAY function, as in:
SELECT TRUNC(NEXT_DAY(SYSDATE, 'MON')) - INTERVAL '7' DAY AS PREV_MONDAY,
TRUNC(NEXT_DAY(SYSDATE, 'FRI')) AS NEXT_FRIDAY
FROM DUAL;
Note that using the above, on weekends the Monday will be the Monday preceding the current date, and the Friday will be the Friday following the current date, i.e. there will be 11 days between the two days.
You can also use
SELECT TRUNC(NEXT_DAY(SYSDATE, 'MON')) - INTERVAL '7' DAY AS PREV_MONDAY,
TRUNC(NEXT_DAY(SYSDATE, 'MON')) - INTERVAL '3' DAY AS NEXT_FRIDAY
FROM DUAL;
in which case the Monday and Friday will always be from the same week, but if SYSDATE is on a weekend the Monday and Friday returned will be from the PREVIOUS week.
Need help figuring out how to determine if the date is the same 'day' as today in teradata. IE, today 12/1/15 Tuesday, same day last year was actually 12/2/2014 Tuesday.
I tried using current_date - INTERVAL'1'Year but it returns 12/1/2014.
You can do this with a bit of math if you can convert your current date's "Day of the week" to a number, and the previous year's "Day of the week" to a number.
In order to do this in Teradata your best bet is to utilize the sys_calendar.calendar table. Specifically the day_of_week column. Although there are other ways to do it.
Furthermore, instead of using CURRENT_DATE - INTERVAL '1' YEAR, it's a good idea to use ADD_MONTHS(CURRENT_DATE, -12) since INTERVAL arithmetic will fail on 2012-02-29 and other Feb 29th leap year dates.
So, putting it together you get what you need with:
SELECT
ADD_MONTHS(CURRENT_DATE, -12)
+
(
(SELECT day_of_week FROM sys_calendar.calendar WHERE calendar_date = CURRENT_DATE)
-
(SELECT day_of_week FROM sys_calendar.calendar WHERE calendar_date = ADD_MONTHS(CURRENT_DATE, -12))
)
This is basically saying: Take the current dates day of week number (3) and subtract from it last years day of week number (2) to get 1. Add that to last year's date and you'll have the same day of the week as current date.
I tested this for all dates between 01/01/2010 and CURRENT_DATE and it worked as expected.
Why don't you simply subtract 52 weeks?
current_date - 364
The SQL below will get you to the abbreviated name for the day of week, it's cumbersome but it works across versions of Teradata.
SELECT CAST(CAST(ADD_MONTHS(CURRENT_DATE, -12) AS DATE FORMAT 'E3') AS CHAR(3)) AS LY_DayOfWeek
, CAST(CAST(CURRENT_DATE) AS DATE FORMAT 'E3') AS CHAR(3)) AS CY_DayOfWeek
Dates are internally represented at integers in Teradata as (Year-1900) * 100000 + (MONTH * 100) + DAY. You may be able to do some creative arithmetic to figure out that 12/1/2015 Tuesday was 12/2/2014 Tuesday last year.
Normally I only show a record, if the actual date is one year later than the date in the database. How can I check if that day is a friday and then show also the records with the date of the saturday or sunday?
For example: Friday the 13th before one year. I will also show records from 14th(saturday) and 15th(sunday)
where myDate.arrival < TRUNC (SYSDATE) -365)
That's my actual statement.
You need a condition in case. Check the example below for reference:
WHERE txnday in
CASE to_char(sysdate, 'Day')
WHEN 'Friday' THEN // Include condition for Sat and Sun as well
ELSE // Include condition for only that day
END
Where Mydate.Arrival < Trunc (Sysdate) - 365
Or (to_char(Mydate.Arrival, 'D') = 6 AND Mydate.Arrival < Trunc (Sysdate) -363)
This way, you can check if that day is a Friday (6th day of week) and then select records whose Arrival value is less than sysdate - 363 (2 more days - Saturday and Sunday - included).
I'm trying to select data grouped by week, which I have working, but I need to be able to specify a different day as the last day of the week. I think something needs to go near INTERVAL (6-weekday('datetime')) but not sure. This kind of SQL is above my pay-grade ($0) :P
SELECT
sum(`value`) AS `sum`,
DATE(adddate(`datetime`, INTERVAL (6-weekday(`datetime`)) DAY)) AS `dt`
FROM `values`
WHERE id = '123' AND DATETIME BETWEEN '2010-04-22' AND '2010-10-22'
GROUP BY `dt`
ORDER BY `datetime`
Thanks!
select
sum(value) as sum,
CASE WHEN (weekday(datetime)<=3) THEN date(datetime + INTERVAL (3-weekday(datetime)) DAY)
ELSE date(datetime + INTERVAL (3+7-weekday(datetime)) DAY)
END as dt
FROM values
WHERE id = '123' and DATETIME between '2010-04-22' AND '2010-10-22'
GROUP BY dt
ORDER BY datetime
This does look pretty evil but, this query will provide you with a sum of value grouped by a week ending on a Thursday (weekday() return of 3).
If you wish to change what day the end of the week is you just need to replace the 3's in the case statement, ie if you wanted Tuesday you would have it say
CASE WHEN (weekday(datetime)<=1) THEN date(datetime + INTERVAL (1-weekday(datetime)) DAY)
ELSE date(datetime + INTERVAL (1+7-weekday(datetime)) DAY)
I hope this helps.
Simple solution that I like. This will return the date for the start of the week assuming the week ends Sunday and starts Monday.
DATE(`datetime`) - INTERVAL WEEKDAY(`datetime`) AS `dt`
This can easily be adjusted to have a week ending on Thursday because Thursday is 3 days earlier than Sunday
DATE(`datetime`) - INTERVAL WEEKDAY(`datetime` + INTERVAL 3 DAY) AS `dt`
this returns for the start of the week that starts on Friday and ends on Thursday.
You can group on this no problem. If you want to use get the end of the week based on the start you do this
DATE(`datetime`) - INTERVAL -6 + WEEKDAY(`datetime` + INTERVAL 3 DAY) AS `dt`
I think you must choose between Sunday and Monday? When you can use DATE_FORMAT for grouping by string format of date, and use %v for grouping by Mondays and %v for grouping by Sundays.
SELECT
sum(`value`) AS `sum`,
DATE_FORMAT(`datetime`,'%v.%m.%Y') AS `dt`
FROM `values`
WHERE id = '123' AND DATETIME BETWEEN '2010-04-22' AND '2010-10-22'
GROUP BY DATE_FORMAT(`datetime`,'%v.%m.%Y')
ORDER BY `datetime`
How to use DATE_FORMAT
I don't remember the exact math, but you can get WEEKDAY to wrap around on different days of the week by adding or subtracting days to its argument. You'll need to tinker with different values of x and y in the expression:
x-weekday(adddate(`datetime`, INTERVAL y DAY))