I have two date from user input. I want to get from jsp these two dates and create a view which shows each day with their own weekday:
2013-3-1 MONDAY
2013-3-2 TUESDAY
2013-3-3 WEDNESDAY
User inputs date and gives to jsp, say:
where start='2013-3-1' and end='2013-3-3'
How to do this?
This can be simpler:
SELECT to_char(d, 'YYYY-MM-DD') AS day_txt
, to_char(d, 'FMDay') AS weekday
FROM generate_series('2013-03-01'::date
, '2013-03-03'::date
, interval '1 day') d;
More details under this later question:
Remove blank-padding from to_char() output
Generally it is better to operate with actual date types, not with text representations, but I did not understand the details or your requirements.
Try this:
select to_char(dt, 'yyyy-mm-dd') as date, to_char(dt, 'Day') as day
from (select ('2013-03-01'::date + i) dt
from generate_series(0,'2013-03-03'::date - '2013-03-01'::date) as t(i)) as t;
You should parameterize the date part of query.
There are a couple of points worth remember in this query.
date calculation: subtract two date become the number of dates between two dates.
generate_series(0, n) generates 0, 1, ... n
Related
Is it possible to convert e.g. string "201701" to dates '2017-01-01' and '2017-01-31' in PostgreSQL?
So for:
"201701" get '2017-01-01' and '2017-01-31'
"201702" get '2017-02-01' and '2017-02-28'
"201703" get '2017-03-01' and '2017-02-31'
etc
You may use the TO_DATE function, and append the day component using string concatenation, something like this:
SELECT
TO_DATE('201702' || '01', 'YYYYMMDD') AS first,
(TO_DATE('201702' || '01', 'YYYYMMDD') + INTERVAL '1 month') -
INTERVAL '1 day' AS last;
The above trick just adds 01 to form the first of the month. For the last day of the same month, it first adds one month to the first, to get the first of the next month, then rolls back one day to get the last of the current month.
Demo
The above answer by Tim Biegeleisen works, but here's an alternative.
The to_date() function converts a string literal to a date value.
sample usage is : to_date(text,format);
SELECT to_date('201701','YYYYMMDD');
(EDITED)
You can also use date_trunc which is a part of PostgreSQL. It does the same as the above one.
select date_trunc('month', current_date) , date_trunc('month', CURRENT_DATE) + interval '1 month - 1 day';
Code Example Live Demo
I need to analyze an SQL query (and construct its equivalent in MDX). I'm not familiar with SQL and can't access the database, so there are 5 simple things I can't figure out:
What does the part WHERE idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 mean? Specifically:
What does subtracting 7 from trunc(SYSDATE, 'iw') do? Subtract 7 weeks or 7 days? I understand the trunc(...) expression is a value 0-53 corresponding to the week of the year, but it seems to clash with the label "previous week" and stated purpose of the query.
How does SQL compare dates? Are the values from trunc(...) evaluated as dates during comparison?
The query seems to group rows together if they happened in the same minute. However, the few rows of output I can see have 10-minute granularity (00:00, 00:10, 00:20, etc.) Is there something in the query that groups rows into 10 minute intervals, or is this a result of the input data?
Why are calls to substr() and to_char() and needed in the group by condition? What would happen if trunc(idate, 'HH24:MI') was used instead?
What does the pm do? There is also a cm that seems to have a similar function. Are these part of the temporary table names?
Finally, how do the hash marks (#) affect this query? I read it might be to signify temporary tables. If so, are these temporary tables created manually, or does something in the query cause them to be created?
For reference here is the query. (On a Oracle database, if it makes any difference.) Its purpose is to "analyze how firewall accept events are trending compared to last week":
SELECT 'Previous Week Average' AS term ,
Substr(To_char(idate, 'HH24:MI'), 0, 4)
|| '0' AS event_time ,
Round(Avg(tot_accept)) AS cnt
FROM (
SELECT *
FROM st_event_100_#yyyymm-1m#
WHERE idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 #stat_monitor_group_query#
UNION ALL
SELECT *
FROM st_event_100_#yyyymm#
WHERE idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 #stat_monitor_group_query# ) pm
GROUP BY substr(to_char(idate, 'HH24:MI'), 0, 4)
|| '0'
UNION ALL
SELECT 'Today' AS term ,
substr(to_char(idate, 'HH24:MI'), 0, 4)
|| '0' AS event_time ,
round(avg(tot_accept)) AS cnt
FROM st_event_100_#yyyymm# cm
WHERE idate >= trunc(SYSDATE) #stat_monitor_group_query#
GROUP BY substr(to_char(idate, 'HH24:MI'), 0, 4)
|| '0'
ORDER BY term DESC,
event_time ASC
iw truncates the date to the first day of the calendar week as defined by the ISO 8601 standard, which is Monday. When you subtract numbers from the date, it is always the number of days. So, idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 gives you those dates that fall between previous week's Monday and Friday.
to_char(idate, 'HH24:MI') gives you the time(hour and minute) part in 24hr format. Ex: 14:33. By using substrin to extract only 4 characters, you are actually getting 14:3. So yes, this groups with a granularity of 10 mins.
You cannot write trunc(idate, 'HH24:MI'). It can only have 1 precision specifier.
If you write trunc(idate,'HH24'), it truncates to the hour. If you use MI, it truncates to the minute. So, to truncate it to 10 mins is a little tricky.
pm is just an alias for the whole subquery.
SELECT *
FROM st_event_100_#yyyymm-1m#
......
WHERE idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 #stat_monitor_group_query#
# is part of the table anme in your query. It has no significance as such. But, it might be project/company specific.
I have a query , where I want to obtain some data for different time durations (this month, this week, etc).
For the "this week" column, I want it to get all the data from the most recent Monday until now. How would I do this?
I have the following SQL so far :
WHERE prla.CREATION_DATE >= SYSDATE - ?
trunc(sysdate, 'iw') is what you're after. IW is the format mask used for Monday of the week the specified date is in (as Monday is the ISO standard for the start of the week). E.g.:
with dates as (select trunc(sysdate, 'mm') - 10 + level dt
from dual
connect by level <= 40)
select dt
from dates
where dt >= trunc(sysdate, 'iw')
and dt <= sysdate; -- only needed if the dates in the column could be beyond now.
Yeah that will do: But it is better to use sysdate-8. Because if the current day is same as your searching day, it will return the current date. For Eg.
select next_day(sysdate-7,'WED') from dual;
OUTPUT
19-AUG-15
Whereas the below one will give you the last week
select next_day(sysdate-8,'WED') from dual;
OUTPUT
12-AUG-15
You should truncate the current date.
TRUNC(SYSDATE, 'DAY')
This should give you the first day of the week, which is Monday in lot of countries.
If it's giving you the previous Sunday instead you should do this.
TRUNC(SYSDATE, 'DAY')+1
I found out to do this now:
select next_day (sysdate-7, 'MONDAY') Last_Monday from dual;
So in my case, we can remove the SYSDATE subtraction and it is simply :
prla.CREATION_DATE >= next_day (sysdate-7, 'MONDAY')
source
Im trying to return the CARDS of my CARD table that will expire in the next month. But the problem is that the table has two columns to represent the card date. The columns are EXPIREDAY and EXPIREMONTH ,both are numbers. So when i do that query i get an error:
select * from CARD WHERE EXPIREDAY <= sysdate - interval '2' DAY;
//Oracle error: ORA-00932: inconsistent datatypes: expected NUMBER got DATE
Is there a way to convert the sysdate - interval '2' DAY as Number data type?
Thanks!
If you want to compare the values as strings you can use this to convert the SYSDATE
SELECT TO_CHAR(sysdate, 'MM') || TO_CHAR(sysdate, 'DD') MONTH_NUM FROM DUAL
-- gives you "0922"
and this for your numeric columns which will pad with leading zeros if you only have a single digit
SELECT TO_CHAR(9, 'FM00') || TO_CHAR(22, 'FM00') MONTH_NUM FROM DUAL
-- also gives you "0922"
If you have control over the table schema it would be best practise to store both the DAY and MONTH values in a single numeric field, so that 9-SEP would be stored in this column as the numeric value 0922 where the month is first so that the natural ordering is used.
A simple and not necessarily very efficient approach is to convert the day and month values into an actual date, using to_date(), and then compare that with your target date range:
select * from card
where to_date(lpad(expireday, 2, '0')
||'/'|| lpad(expiremonth, 2, '0'), 'DD/MM')
between sysdate and add_months(sysdate, 1);
Which appears to work. But this will have problems if the dates span the end of the year. Because your table doesn't specify the year, you either have to work one out, or allow to_date to default it to the current year. And if you let it default then it won't work. For example, if you have values for December and January in your table, and run this query in December, then the January dates will be seen as January 2014, and won't be counted as being in the next month. So you'll need to do more to pick the right year.
This treats any month numbers before the current one as being next year, which may be good enough for you as you only have a one-month window:
select * from card
where to_date(lpad(expireday, 2, '0')
||'/'|| lpad(expiremonth, 2, '0')
||'/'|| (extract(year from sysdate) +
case when expiremonth < extract(month from sysdate) then 1 else 0 end),
'DD/MM/YYYY')
between sysdate and add_months(sysdate, 1);
SQL Fiddle using a date range from December to January.
And you can see the ways the two columns are being combined to form a date in this Fiddle.
As so often, the moral is... store things as the right data type. Store dates as dates, not as string or numbers.
Im trying to return the CARDS of my CARD table that will expire in the next month. But the problem is that the table has two columns to represent the card date.
Assuming:
you are using floating months (say: from 23 dec. to 23 jan.) and
your table somehow only contains one (floating ?) year of data
Why can't you use simple arithmetics? Like that:
-- some constant definitions for testing purpose
with cst as (
select EXTRACT(DAY from TO_DATE('23/12','DD/MM')) as theDay,
EXTRACT(MONTH from TO_DATE('23/12','DD/MM')) as theMonth
from dual)
-- the actual query
select card.* from card,cst
where (expiremonth = theMonth AND expireday > theDay)
or (expiremonth = 1+MOD(theMonth,12) AND expireday <= theDay);
-- ^^^^^^^^^^^^^^^^^^
-- map [01 .. 12] to [02 .. 12, 01] (i.e.: next month)
This will simply select all "pseudo-dates" from tomorrow to the end of the month, as well as any one before (and including) the current day# next month.
See this example.
For something a little bit more generic, but probably more efficient than converting all your values TO_DATE, you might want to try that:
-- the calendar is the key part of the query (see below)
with calendar as (
select extract(month from sysdate + level) as theMonth,
extract(day from sysdate + level) as theDay
from DUAL connect by ROWNUM <= 8)
-- ^
-- adjust to the right number of days you are looking for
select card.* from card join calendar
on expiremonth = theMonth and expireDay = theDay
The idea here is to simply build a calendar with all the upcoming days and then join your data table on that calendar. See an example here.
Try using to_char(sysdate - interval '2' DAY,'ddmmyyyy') to convert to character type. The date format('ddmmyyyy') will depend of the value of expiredate
Hallo all, i have this SQL
SELECT DATE_ADD( '2009-'+ MONTH( NOW() ) +'-01' , INTERVAL -1 MONTH );
i can't get it to work, what i make wrong here?
tanks for help.
SELECT CONCAT_WS('-', '2009', MONTH(NOW()), '01') - INTERVAL 1 MONTH
It's the concatenation of the date that doesn't work. It converts the strings to numbers, so you get 2009+11+-1 = 2019, which then fails to convert to a date.
Instead of concatenating a date from strings, you can use the last_day function to get the last day of the current month, add one day to get to the next day of the next month, then subtract two months to get to the first day of the previous month:
select last_day(now()) + interval 1 day - interval 2 month;
Plus is an arithmetical operator, you have to use concat.
SELECT DATE_ADD( concat('2009-',MONTH(NOW()),'-01') , INTERVAL -1 MONTH )
or better
select date(now()) - interval day(NOW())-1 day - interval 1 month;
(this will also work in 2010)