How to compare date (month/year) with another date (month/year) - sql

How to compare parts of date in Informix DBMS:
I wrote the following query but I get a syntax error:
select id,name from h_history
where ( h_date::DATETIME YEAR TO MONTH >= 2/2012 )
and ( h_date::DATETIME YEAR TO MONTH <= 1/2013 )
I wanna to compare with year and month
How to do this?

If you're going to use the DATETIME route, you need to format the values correctly. As it is, you've written:
select id,name from h_history
where ( h_date::DATETIME YEAR TO MONTH >= 2/2012 )
and ( h_date::DATETIME YEAR TO MONTH <= 1/2013 )
The 2/2012 is an integer division equivalent to 0, and there's no implicit cast from integers to datetime or vice versa.
You could write:
-- Query 1
SELECT id, name
FROM h_history
WHERE (h_date::DATETIME YEAR TO MONTH >= DATETIME(2012-02) YEAR TO MONTH)
AND (h_date::DATETIME YEAR TO MONTH <= DATETIME(2013-01) YEAR TO MONTH)
That's verbose but precise. You could use a short-cut:
-- Query 2
SELECT id, name
FROM h_history
WHERE (h_date::DATETIME YEAR TO MONTH >= '2012-02')
AND (h_date::DATETIME YEAR TO MONTH <= '2013-01')
However, since h_date is a DATE rather than a DATETIME X TO Y type, there are other options available. The YEAR, MONTH, DAY functions extract the obvious parts from a DATE (and, if you pass a DATETIME to the function, then the DATETIME will be coerced into a DATE and then processed). The only fully locale-independent DATE constructor is the MDY function which takes 3 arguments, the month, day and year. All string representations are subject to interpretation by locale, and therefore won't work everywhere all the time.
You can also do:
-- Query 3
SELECT id, name
FROM h_history
WHERE (h_date >= MDY(2, 1, 2012))
AND (h_date <= MDY(1, 31, 2013))
Or:
-- Query 4
SELECT id, name
FROM h_history
WHERE ((YEAR(h_date) = 2012 AND MONTH(h_date) >= 2) OR YEAR(h_date) >= 2013)
AND ((YEAR(h_date) = 2013 AND MONTH(h_date) <= 1) OR YEAR(h_date) < 2013)
Or:
-- Query 5
SELECT id, name
FROM h_history
WHERE (YEAR(h_date) * 100 + MONTH(h_date)) >= 201202
AND (YEAR(h_date) * 100 + MONTH(h_date)) <= 201301
Given the choice, I'd probably use Query 2 as succinct but accurate, or perhaps Query 5, but all of queries 1-5 are usable.
If h_date was a column of some DATETIME type and you need to compare parts of a DATETIME, you can either use casting (as shown in Query 1) or the EXTEND function. That tends to be verbose.

Assuming that h_date is a proper date field, why wouldn't
SELECT
id
, name
FROM h_history
WHERE h_date >= '02/01/2012' and h_date <= '01/31/2013'
work for you?

Try the following
SELECT
id,name
FROM h_history
WHERE
h_date >= MDY(2, 1, 2012)
AND h_date < MDY(2, 1, 2013)

select * from table t where MONTH(t.date) = 12

Related

How to SELECT year using a WHERE clause on a DateTime type column

There is a table Saleswith data from year 2005 to year 2015
I want to query data and filter column Sales_Date with datetime data type where the year is 2013 including all other columns
SELECT *
FROM Sales
WHERE Sales_Date BETWEEN '2013-01-01' AND '2014-01-01';
is this correct or there is a function specifically to filter query by year of datetime data type
It is not correct as it includes data from 2014-01-01, which isn't a day in 2013.
Instead of between, which checks for a closed interval, use >= and < to search for an interval open at the end.
SELECT *
FROM sales
WHERE sales_date >= '2013-01-01'
AND sales_date < '2014-01-01';
You could also use year() or datepart() to extract the year like in
...
WHERE year(sales_date) = 2013;
or
...
WHERE datepart(year, sales_date) = 2013;
But that will prevent any index on sales_date to be used, which isn't good in terms of performance. So it's not the best way to query that data, the first approach with >= and < is to be preferred.
You can use the year function:
SELECT *
FROM Sales
WHERE YEAR(Sales_Date) = 2013;

Looking for a birthdate Range in SQL

I am looking for a birthdate range of march 21 to April 20, and the year doesn't matter. and it seems that when i search other months and date are coming out.
select * from AllStudentInfo Where
((MONTH(birthDate) >= 3 and day(birthDate) >= 21)
OR
(MONTH(birthDate) <= 4 and day(birthDate) <= 20))
AND
(BGRStatus = 'S' OR BGRStatus = 'SL')
Chances are that you want to discover up & coming dates. In any case, you can create a virtual date as follows:
SELECT DATEFROMPARTS (2017,month(birthDate),day(birthDate) as birthday
FROM AllStudentInfo
In this case, you can use:
SELECT *
FROM AllStudentInfo
WHERE DATEFROMPARTS (2017,month(birthDate),day(birthDate)
BETWEEN '2017-03-21' AND '2017-04-20';
The year 2017 is arbitrary. The point is that the dates in the BETWEEN clause are in the same year.
Using more modern techniques, you can combine it as follows:
WITH cte AS(
SELECT *,DATEFROMPARTS (2017,month(birthDate),day(birthDate) as birthDay
FROM AllStudentInfo
)
SELECT * FROM cte WHERE birthDay BETWEEN '2017-03-21' AND '2017-04-20';
The cte is a Common Table Expression which is an alternative to using a sub query.
Here is an alternative which is closer to the spirit of the question. You can use the format function to generate an expression which is purely month & day:
format(birthDate,'MM-dd')
The MM is MSSQL’s way of saying the 2-digit month number, and dd is the 2-digit day of the month.
This way you can use:
format(birthDate,'MM-dd') BETWEEN '03-21' AND '04-20'
Again as a CTE:
WITH cte AS(
SELECT *,format(birthDate,'MM-dd') as birthDay
FROM AllStudentInfo
)
SELECT * FROM cte WHERE birthDay BETWEEN '03-21' AND '04-20';
You should get the same results, but the year is completely ignored.
Switch your statement to AND
Like so
select * from AllStudentInfo Where
((MONTH(birthDate) >= 3 and day(birthDate) >= 21)
AND --Make the change here
(MONTH(birthDate) <= 4 and day(birthDate) <= 20))
AND
(BGRStatus = 'S' OR BGRStatus = 'SL')
Using OR you are querying anything that is in that date range OR that month range. And therefore, you would get results from other every months.

Return just the last day of each month with SQL

I have a table that contains multiple records for each day of the month, over a number of years. Can someone help me out in writing a query that will only return the last day of each month.
SQL Server (other DBMS will work the same or very similarly):
SELECT
*
FROM
YourTable
WHERE
DateField IN (
SELECT MAX(DateField)
FROM YourTable
GROUP BY MONTH(DateField), YEAR(DateField)
)
An index on DateField is helpful here.
PS: If your DateField contains time values, the above will give you the very last record of every month, not the last day's worth of records. In this case use a method to reduce a datetime to its date value before doing the comparison, for example this one.
The easiest way I could find to identify if a date field in the table is the end of the month, is simply adding one day and checking if that day is 1.
where DAY(DATEADD(day, 1, AsOfDate)) = 1
If you use that as your condition (assuming AsOfDate is the date field you are looking for), then it will only returns records where AsOfDate is the last day of the month.
Use the EOMONTH() function if it's available to you (E.g. SQL Server). It returns the last date in a month given a date.
select distinct
Date
from DateTable
Where Date = EOMONTH(Date)
Or, you can use some date math.
select distinct
Date
from DateTable
where Date = DATEADD(MONTH, DATEDIFF(MONTH, -1, Date)-1, -1)
In SQL Server, this is how I usually get to the last day of the month relative to an arbitrary point in time:
select dateadd(day,-day(dateadd(month,1,current_timestamp)) , dateadd(month,1,current_timestamp) )
In a nutshell:
From your reference point-in-time,
Add 1 month,
Then, from the resulting value, subtract its day-of-the-month in days.
Voila! You've the the last day of the month containing your reference point in time.
Getting the 1st day of the month is simpler:
select dateadd(day,-(day(current_timestamp)-1),current_timestamp)
From your reference point-in-time,
subtract (in days), 1 less than the current day-of-the-month component.
Stripping off/normalizing the extraneous time component is left as an exercise for the reader.
A simple way to get the last day of month is to get the first day of the next month and subtract 1.
This should work on Oracle DB
select distinct last_day(trunc(sysdate - rownum)) dt
from dual
connect by rownum < 430
order by 1
I did the following and it worked out great. I also wanted the Maximum Date for the Current Month. Here is what I my output is. Notice the last date for July which is 24th. I pulled it on 7/24/2017, hence the result
Year Month KPI_Date
2017 4 2017-04-28
2017 5 2017-05-31
2017 6 2017-06-30
2017 7 2017-07-24
SELECT B.Year ,
B.Month ,
MAX(DateField) KPI_Date
FROM Table A
INNER JOIN ( SELECT DISTINCT
YEAR(EOMONTH(DateField)) year ,
MONTH(EOMONTH(DateField)) month
FROM Table
) B ON YEAR(A.DateField) = B.year
AND MONTH(A.DateField) = B.Month
GROUP BY B.Year ,
B.Month
SELECT * FROM YourTableName WHERE anyfilter
AND "DATE" IN (SELECT MAX(NameofDATE_Column) FROM YourTableName WHERE
anyfilter GROUP BY
TO_CHAR(NameofDATE_Column,'MONTH'),TO_CHAR(NameofDATE_Column,'YYYY'));
Note: this answer does apply for Oracle DB
Here's how I just solved this. day_date is the date field, calendar is the table that holds the dates.
SELECT cast(datepart(year, day_date) AS VARCHAR)
+ '-'
+ cast(datepart(month, day_date) AS VARCHAR)
+ '-'
+ cast(max(DATEPART(day, day_date)) AS VARCHAR) 'DATE'
FROM calendar
GROUP BY datepart(year, day_date)
,datepart(month, day_date)
ORDER BY 1

Retrieve records in a range using year and month fields in mysql

I have a mysql table with year (YEAR(4)) and month (TINYINT) columns. What is the best way to select all records in a range, e.g. 2009-10 .. 2010-02 ?
Here is a generalized solution:
Select...
From ...
Where ( Year = <StartYear> And Month >= <StartMonth> )
Or ( Year > <StartYear> And Year < <EndYear> )
Or ( Year = <EndYear> And Month <= <EndMonth> )
Use:
WHERE (year = 2009 AND month >= 10)
OR (year = 2010 AND month <= 2)
...or, using UNION ALL:
SELECT t.*
FROM TABLE t
WHERE year = 2009 AND month >= 10
UNION ALL
SELECT t.*
FROM TABLE t
WHERE year = 2010 AND month <= 2
UNION ALL is faster than UNION, but won't remove duplicates.
You may want to consider creating another column called year_month which stores the date in YYYYMM format, such as 201002 for February 2010. Make sure to create an index on it, and you may also create two triggers to automatically update the column ON INSERT and ON UPDATE.
Then you would be able to use:
WHERE year_month BETWEEN 200710 AND 201002
This would use the index on year_month.
The accepted answer does not work for the case when StartYear and EndYear is the same. E.g. 2017-01 to 2017-08. A revised psuedocode that covers this case would be
((year = StartYear and month >= StartMonth) OR (year > StartYear)) AND ((year = EndYear and month <= EndMonth) OR (year < EndYear))

How do I get a maximium daily value of a numerical field over a year in SQL

How do I get a maximium daily value of a numerical field over a year in MS-SQL
This would query the daily maximum of value over 2008:
select
datepart(dayofyear,datecolumn)
, max(value)
from yourtable
where '2008-01-01' <= datecolumn and datecolumn < '2009-01-01'
group by datepart(dayofyear,datecolumn)
Or the daily maximum over each year:
select
datepart(year,datecolumn),
, datepart(dayofyear,datecolumn)
, max(value)
from yourtable
group by datepart(year,datecolumn), datepart(dayofyear,datecolumn)
Or the day(s) with the highest value in a year:
select
Year = datepart(year,datecolumn),
, DayOfYear = datepart(dayofyear,datecolumn)
, MaxValue = max(MaxValue)
from yourtable
inner join (
select
Year = datepart(year,datecolumn),
, MaxValue = max(value)
from yourtable
group by datepart(year,datecolumn)
) sub on
sub.Year = yourtable.datepart(year,datecolumn)
and sub.MaxValue = yourtable.value
group by
datepart(year,datecolumn),
datepart(dayofyear,datecolumn)
You didn't mention which RDBMS or SQL dialect you're using. The following will work with T-SQL (MS SQL Server). It may require some modifications for other dialects since date functions tend to change a lot between them.
SELECT
DATEPART(dy, my_date),
MAX(my_number)
FROM
My_Table
WHERE
my_date >= '2008-01-01' AND
my_date < '2009-01-01'
GROUP BY
DATEPART(dy, my_date)
The DAY function could be any function or combination of functions which gives you the days in the format that you're looking to get.
Also, if there are days with no rows at all then they will not be returned. If you need those days as well with a NULL or the highest value from the previous day then the query would need to be altered a bit.
Something like
SELECT dateadd(dd,0, datediff(dd,0,datetime)) as day, MAX(value)
FROM table GROUP BY dateadd(dd,0, datediff(dd,0,datetime)) WHERE
datetime < '2009-01-01' AND datetime > '2007-12-31'
Assuming datetime is your date column, dateadd(dd,0, datediff(dd,0,datetime)) will extract only the date part, and then you can group by that value to get a maximum daily value. There might be a prettier way to get only the date part though.
You can also use the between construct to avoid the less than and greater than.
Group on the date, use the max delegate to get the highest value for each date, sort on the value, and get the first record.
Example:
select top 1 theDate, max(theValue)
from TheTable
group by theDate
order by max(theValue) desc
(The date field needs to only contain a date for this grouping to work, i.e. the time component has to be zero.)
If you need to limit the query for a specific year, use a starting and ending date in a where claues:
select top 1 theDate, max(theValue)
from TheTable
where theDate between '2008-01-01' and '2008-12-13'
group by theDate
order by max(theValue) desc