I am junior in informix and I am struggling with some date calculations. I have this code snippet in my source:
LET dtRefDate = TODAY;
IF extend( CURRENT, hour to hour ) BETWEEN '06' AND '23' THEN LET
dtRefDate = TODAY+1;
END IF;
Can anybody explain me please what does extend function do? and what is the difference between CURRENT and TODAY? I didn't comprehend the difference from IBM official documentation.
Thank you
TODAY returns a DATE value, CURRENT returns a DATETIME (YEAR TO FRACTION (3)) value.
EXTEND is used to extract individual parts of a DATE or DATETIME value.
Look at this examples:
> select CURRENT from table(set{1});
(expression)
2019-09-29 10:07:12.000
1 row(s) retrieved.
> select TODAY from table(set{1});
(expression)
09/29/2019
1 row(s) retrieved.
> select extend(TODAY,year to year) from table(set{1});
(expression)
2019
1 row(s) retrieved.
> select extend(TODAY,month to day) from table(set{1});
(expression)
09-29
1 row(s) retrieved.
> select extend(CURRENT,hour to minute) from table(set{1});
(expression)
10:08
1 row(s) retrieved.
>
Related
I have a database column giving me information on how often a file comes in.
Frequency_month
-------------
3rd Friday of the month
2nd Tuesday of the month
3rd Thursday of the month
I need to update this column and have it be a timestamp. e.g.
Frequency_month
-------------
2020-05-21 00:00:00
2020-05-11 00:00:00
2020-05-20 00:00:00
How can I accomplish this using postgres PLPGSQL language?
The following yields what your looking for. As far a parsing the Frequency_month it imposes the following restrictions:
The first character in the string is a digit indicating the relative
number.
This is followed 2 characters ordinal spec (st, nd, etc) and a space.
Actually any 3 characters, they are not checked.
Position 5 - 7 con the first 3 characters of the English day of week (dow).
If any of those are not satisfied you will need to change the S1 subquery.
Further it requires you to provide a date of reference. This may be any date in the month of interest. See comment by #sddk.
It proceeds as follows:
Parse the above extracting the week number, day of week, and last
day of the prior month. (S1).
Determine the ISODOW id numbers for the day of week specified and
DOW for last of prior month. (S2).
Using the ISODOW id numbers Determine, determine the first
occurrence of the target day in the target month. (S3).
Adjust the date from #3 by the additional weeks. (S4).
Finally, if the resulting date in #4 in still in the target month
return the date form #4. If it is not the same month then return
null. This occurs when there in no nth dow in the month or the dow
is incorrectly specified.
I have wrapped the above into a SQL function making parameterization easy. See Demo.
create or replace
function frequency_month( frequency_string text
, target_month date
)
returns date
language sql
as $$
with day_names( l_days) as
( values (array['mon','tue','wed','thu','fri','sat','sun']) )
select -- if the calculated date in still in the target month return that date else return null
-- covers invalid week in frequency 6th Friday or 0th Monday
case when extract(month from target_date) = extract (month from target_month)
then target_date
else null
end
from ( -- Advance from first dow in month the number of weeks to desirded dates
--select (first_of_mon + (7*(rel_num-1)) * interval '1 day')::date target_date
select (first_of_mon + (rel_num-1) * interval '1 week')::date target_date
from ( -- with last day of prior month get first DOW week of target month
select case when dow_day_nbr <= from_day_nbr
then (from_date + (dow_day_nbr-from_day_nbr+7) * interval '1 days' )::date
else (from_date + (dow_day_nbr-from_day_nbr) * interval '1 days' )::date
end first_of_mon
, rel_num
from ( -- Pick up ISODOW numbers
select array_position(l_days, (substring(to_char(from_date, 'day'),1,3))) as from_day_nbr
, array_position(l_days, lower(substring(rel_dow,1,3))) as dow_day_nbr
, from_date
, rel_num
from day_names
cross join ( -- get last day of prior month, desired relative day, relative dow
select substr(frequency_string,1,1)::integer rel_num
, lower(substr(frequency_string,5,3)) rel_dow
, (date_trunc('month',target_month) - interval '1 day')::date from_date
) s1
) s2
) s3
) s4;
$$;
Note: The demo also includes a standalone version if a function is not desired.
I am currently writing a Crystal Report that has a DB2 query as its backend. I have finished the query but am stuck on the date portion of it. I am going to be running it twice a month - once on the 16th, and once on the 1st of the next month. Here's how it should work:
If I run it on the 16th of the month, it will give me results from the 1st of that same month to the 15th of that month.
If I run it on the 1st of the next month, it will give me results from the 16th of the previous month to the last day of the previous month.
This comes down a basic bi-monthly report. I've found plenty of hints to do this in T-SQL, but no efficient ways on how to accomplish this in DB2. I'm having a hard time wrapping my head around the logic to get this to consistently work, taking into account differences in month lengths and such.
There are 2 expressions for start and end date of an interval depending on the report date passed, which you may use in your where clause.
The logic is as follows:
1) If the report date is the 1-st day of a month, then:
DATE_START is 16-th of the previous month
DATE_END is the last day of the previous month
2) Otherwise:
DATE_START is 1-st of the current month
DATE_END is 15-th of the current month
SELECT
REPORT_DATE
, CASE DAY(REPORT_DATE) WHEN 1 THEN REPORT_DATE - 1 MONTH + 15 ELSE REPORT_DATE - DAY(REPORT_DATE) + 1 END AS DATE_START
, CASE DAY(REPORT_DATE) WHEN 1 THEN REPORT_DATE - 1 ELSE REPORT_DATE - DAY(REPORT_DATE) + 15 END AS DATE_END
FROM
(
VALUES
DATE('2020-02-01')
, DATE('2020-02-05')
, DATE('2020-02-16')
) T (REPORT_DATE);
The result is:
|REPORT_DATE|DATE_START|DATE_END |
|-----------|----------|----------|
|2020-02-01 |2020-01-16|2020-01-31|
|2020-02-05 |2020-02-01|2020-02-15|
|2020-02-16 |2020-02-01|2020-02-15|
In Db2 (for Unix, Linux and Windows) it could be a WHERE Condition like
WHERE
(CASE WHEN date_part('days', CURRENT date) > 15 THEN yourdatecolum >= this_month(CURRENT date) AND yourdatecolum < this_month(CURRENT date) + 15 days
ELSE yourdatecolum > this_month(CURRENT date) - 1 month + 15 DAYS AND yourdatecolum < this_month(CURRENT date)
END)
Check out the THIS_MONTH function - there are multiple ways to do it. Also DAYS_TO_END_OF_MONTH might be helpful
I am trying to write a piece for a query that grabs the closest, past June 1st. For example, today is 10/2/2018. If I run the query today, I need it to use the date 6/1/2018. If I run it on 5/29/2019, it still needs to grab 6/1/2018. If I run it on 6/2/2019, it should then grab 6/1/2019. If I run it on 6/2/2022, it should then grab 6/1/2022 and so on.
I believe I need to start with something like this:
SELECT CASE WHEN EXTRACT(MONTH FROM NOW())>=6 THEN 'CURRENT' ELSE 'RF LAST' END AS X
--If month is greater than or equal to 6, you are in the CURRENT YEAR (7/1/CURRENT YEAR)
--If month is less than 6, then reference back to the last year (YEAR MINUS ONE)
And I believe I need to truncate the date then perform an operation. I am unsure of which approach to take (if I should be adding a year to a timestamp such as '6/1/1900', or if I should try to disassemble the date parts to perform an operation. I keep getting errors in my attempts such as "operator does not exist". Things I have tried include:
SELECT (CURRENT_DATE- (CURRENT_DATE-INTERVAL '7 months'))
--This does not work as it just gives me a count of days.
SELECT (DATE_TRUNC('month',NOW())+TIMESTAMP'1900-01-01 00:00:00')
--Variations of this just don't work and generally error out.
Use a case expression to determine if you need to use the current year, or, the previous year (months 1 to 5)
case when extract(month from current_date) >= 6 then 0 else -1 end
then add that to the year extracted from current_date, e.g. using to_date()
select to_Date('06' || (extract(year from current_date)::int + case when extract(month from current_date) >= 6 then 0 else -1 end)::varchar, 'mmYYYY');
You could also use make_date(year int, month int, day int) in postgres 9.4+
select make_date(extract(year from current_date) + case when extract(month from current_date) >= 6 then 0 else -1 end, 6, 1) ;
If month lower than 6, trunc year and minus 6 months.
Else trunc year and add 6 months.
set datestyle to SQL,MDY;
select
case when (extract( month from (date::date)))<6 then date_trunc('year',date)-'6 month'::interval
else date_trunc('year',date)+'6 months'::interval
end as closest_prev_june,
another_column,
another_column2
from mytable;
But format is default and supposed you have a column that named date.
If you want to do this with now(), change date columns with now()
function.
I want to just select data for the current week. So...
If the current date is a Monday just select Monday
If the current date is a Tuesday select Monday and Tuesday's data
If the current date is Wednesday select Monday, Tuesday and Wednesday
...and so on. I want it to reset on Sunday and I believe it's some kind of "where" clause just don't know what. As you can see below I'm just counting the number of pieces into the oven and want it to accumulate as the week goes on and then reset on Sunday.
select
count(*) as PiecesIntoOven
from ovenfeederfloat
where...??
Thanks for the help.
If you're looking to do this in Sql Server, see below. Essentially this converts the current date to its numeric (0-6) value, then finds the 0th date for that week and uses it to set the lower bound of the where clause.
select sum(numberofpieces)
from Test
where dateofwork <= getdate()
and dateofwork >= (DATEADD(DAY, DATEPART(WEEKDAY,getdate()) * -1, getdate()) + 1)
Note that the '0' value is impacted by DATEFIRST. https://stackoverflow.com/a/1113891/4824030
I'm not certain how to do this in Oracle. Something like the below should work, but it's being finicky in sqlfiddle.
select sum(numberofpieces)
from Test
where dateofwork <= current_timestamp
and dateofwork >= (((to_char(level+trunc(current_timestamp,'D'),'Day') * -1) + current_timestamp) + 1)
On Pavel's page is the following function:
CREATE OR REPLACE FUNCTION makedate(year int, dayofyear int)
RETURNS date AS $$
SELECT (date '0001-01-01' + ($1 - 1) * interval '1 year' + ($2 - 1) * interval '1 day'):: date
$$ LANGUAGE sql;
I have the following code:
makedate(y.year,1)
What is the fastest way in PostgreSQL to create a date for January 1st of a given year?
Pavel's function would lead me to believe it is:
date '0001-01-01' + y.year * interval '1 year' + interval '1 day';
My thought would be more like:
to_date( y.year||'-1-1', 'YYYY-MM-DD');
Am looking for the fastest way using PostgreSQL 8.4. (The query that uses the date function can select between 100,000 and 1 million records, so it needs speed.)
Thank you!
I would just use the following, given that year is a variable holding the year, instead of using a function:
(year || '-01-01')::date
Btw. I can't believe that this conversion is your bottleneck. But maybe you should have a look at generate_series here (I don't know your usecase).
select current_date + s.a as dates from generate_series(0,14,7) as s(a);
dates
------------
2004-02-05
2004-02-12
2004-02-19
(3 rows)
Using to_date() is even simpler than you expect:
> select to_date('2008','YYYY');
to_date
------------
2008-01-01
(1 row)
> select to_date(2008::text,'YYYY');
to_date
------------
2008-01-01
(1 row)
Note that you still have to pass the year as a string, but no concatenation is needed.
As suggested by Daniel, in the unlikely case that this conversion is a bottleneck, you might prefer to precompute the function and store in a table. Eg:
select ynum, to_date( ynum ||'-01-01', 'YYYY-MM-DD') ydate
from generate_series(2000,2009) as ynum;
If there are a few years (and hence no need of indexes), you might even create the table dinamically for the scope of each query, with the new WITH.