How can I count and group by between two date? - sql

For example,
the start date = '20100530' and
the end date = '20100602'
How can I write a SQL to display the below result?
month: may, 2 days
month: June, 2 days

Use a recursive CTE to generate all of the dates between the start and end, and then do a simple group and count (caution, not tested, but should be close if not exactly right):
with dates (the_date) as (
select #start_date
UNION ALL
select dateadd(dd, 1, the_date) from dates where the_date <= #end_date
)
select
datepart(mm, the_date) month,
count(*) num_days
from
dates
group by
datepart(mm, the_date)

TBH, you really need to provide more schema and information about the source data. However, given what little we know, you should be able to write:
Select DateName('month', [Date]) As Month
, Cast(DateDiff(d, #StartDate, #EndDate) As varchar(10) - 1) + ' days'
From Table
Where [Date] Between #StartDate And #EndDate
What we'd need to know to refine our solutions is exactly how 2 days is supposed to be calculated. Is it the days between the start and end date? Is it the day of the second parameter?

Related

How to select data's from this today,week and month sperately?

I have a problem that is I am unable to resolve as of now.
I need to get the data of
this day, this week and this month
I have a table reminder where I want to select reminders according to
following parameters.
1. Today
2. This Week
3. This Month
The column rdate having the date format in dd-mm-yyyy which is stored as nvarchar
For example
If I execute this weeks query I should get data starting from this week i.e.
If it is Friday I should get data from starting from Sunday to Saturday of that week
How can I get the data as mentioned above. I have searched a lot on internet but I didn't get the solution?
This is the query I have been trying
SELECT
*
FROM
reminder
WHERE
date > DATE_SUB(GETDATE(), INTERVAL 1 DAY)
ORDER BY
rdate DESC;
Where I'm converting nvarchar to date format.
If it's not possible to change the [date] column's data type to DATE, then you will incur a massive performance penalty when trying to filter by date.
Add computed column to table
We can add a computed column that will store the date in the correct format, and then index it for quick searchiing:
ALTER TABLE reminder
ADD Date_Value AS (CONVERT(DATE, '12-05-2016', 105)) PERSISTED;
-- This should yield superior performance
CREATE NONCLUSTERED INDEX IX_Date_Value ON reminder (Date_Value);
Table-valued function to calculate date range
Now, let's create an inline table-valued function to generate the date range for specific period types:
CREATE FUNCTION [dbo].[tvfn_Get_Date_Range](
#Period_Type VARCHAR(100)
)
RETURNS
TABLE
AS RETURN
(
WITH date_range AS(
SELECT CAST(GETDATE() AS DATE) d
-- This line works correctly if your week starts on Sunday
,CAST(DATEADD(WEEK, DATEDIFF(WEEK, '19050101', GETDATE()), '19050101') AS DATE) AS week_start
,CAST(DATEADD(DAY, - DAY(GETDATE()) + 1, GETDATE()) AS DATE) AS month_start
,CAST(DATEADD(MONTH, 1, DATEADD(DAY, - DAY(GETDATE()), GETDATE())) AS DATE) AS month_end
)
SELECT d AS From_Date
,d AS To_Date
FROM date_range
WHERE #Period_Type = 'DAY'
UNION ALL
SELECT week_start
,DATEADD(DAY, 7, week_start)
FROM date_range
WHERE #Period_Type = 'WEEK'
UNION ALL
SELECT month_start
,month_end
FROM date_range
WHERE #Period_Type = 'MONTH'
)
In the above function, week starts on Sunday. If you need this to be configurable, then take a look at the answer to SET DATEFIRST in FUNCTION.
Fast, simple querying now possible
You can now use the two together using a simple query:
SET #Range VARCHAR(100) = 'WEEK'
SELECT *
FROM reminder
CROSS APPLY [dbo].[tvfn_Get_Date_Range](#Range) dr
WHERE Date_Value BETWEEN dr.Date_From AND dr.Date_To
If you can't change the columns data type to Date (or DateTime), you must convert it to date in the query.
Here is one way to get the data for today, this week and this month:
Get records from today:
SELECT *
FROM reminder
WHERE CONVERT(Date, [date], 105) = CAST(GETDATE() as date)
ORDER BY rdate DESC;
Get records from this week:
SELECT *
FROM reminder
WHERE DATEPART(WEEK, CONVERT(Date, [date], 105)) = DATEPART(WEEK, GETDATE())
AND DATEPART(YEAR, CONVERT(Date, [date], 105)) = DATEPART(YEAR, GETDATE())
ORDER BY rdate DESC;
Get records from this Month:
SELECT *
FROM reminder
WHERE DATEPART(MONTH, CONVERT(Date, [date], 105)) = DATEPART(MONTH, GETDATE())
AND DATEPART(YEAR, CONVERT(Date, [date], 105)) = DATEPART(YEAR, GETDATE())
ORDER BY rdate DESC;
To my knowledge, SQL server internally deals with date format as MM/dd/yyyy.
Usually I prefer to save date as string in SQL table since it's easier for inserting and retrieving.
For example, suppose that the column rdate is defined as follows in your table reminder:
[rdate] nvarchar NULL
Then you can customize the select statement for a week as follows:
"Select R.* From reminder R Where CAST(R.rdate as datetime) between
'03/04/2011' AND '03/11/2011'"
And for 10 days as follows:
"Select R.* From reminder R Where CAST(R.rdate as datetime) between
'03/04/2011' AND '03/14/2011'"
And so on. If this is not what you want, please provide more details about your requirements.

Function to go back 2 years, first day of last month

I'm hoping to find a solution for this to automate a report I have. Basically what I'm trying to accomplish here is grabbing a date (first day of previous month, two years ago through last day of previous month current year).
So the date span if running this month would look like this: between 4/1/2013 and 3/31/2015
I have found code to get the date two years ago but I'm not able to also incorporate the month functions... Any help is very much appreciated!
For year I'm using this:
SELECT CONVERT(VARCHAR(25),DATEADD(year,-2,GETDATE()),101)
First day of previous month 2 years ago:
SELECT CONVERT(DATE,dateadd(day, -1, dateadd(day, 1 - day(GETDATE()), GETDATE())))
Last day of last month:
SELECT CONVERT(DATE,DATEADD(month, DATEDIFF(month, 0, DATEADD(year,-2,GETDATE())), 0))
Then just do whatever logic you need with them
Your where clause can look something like this:
where date >= cast(dateadd(year, -2,
dateadd(month, -1, getdate() - day(getdate()) + 1)
) as date) and
date < cast(getdate() - day(getdate()) + 1 as date)
This makes use of the handy convenience that subtracting/adding a number to a datetime is the same as adding a date. The start date says: get the first day of the month, then subtract one month, then subtract two years. This could have been done as dateadd(month, -25, . . .), but I think separating the logic is clearer.
This gives you two dates you are looking for:
SELECT
CAST((DATEADD(yy, -2, DATEADD(d, -1 * DATEPART(dd, getdate()) + 1 , GETDATE() ))) as date) as yourTwoYearsAgoDate,
CAST((DATEADD(d, -1 * DATEPART(dd, GETDATE()), GETDATE())) as date) as yourEndOfLastMonthDate
Given a reference date (e.g. "today"),
declare #today date = '23 April 2015'
The 1st of the month is computed by subtracting 1 less than the day number of the current month:
select first_of_current_month = dateadd(day,1-day(#today),#today)
The last day of the previous month is day 0 of the current month, so to get the last day of the previous month, just subtract the current day number:
select last_of_previous_month = dateadd(day,-day(#today),#today)
Moving two years back is easy:
select two_years_back = dateadd(year,-2, #today )
Putting it all together, this should do you:
declare #today date = '23 April 2015'
select *
first_day_of_current_month = dateadd(day,1-day(#today),#today),
last_day_of_previous_month = dateadd(day, -day(#today),#today) ,
date_from = dateadd(year,-2, dateadd(day,1-day(#today),#today) ) ,
date_thru = dateadd(day, -day(#today),#today)
yielding the expected results:
first_day_of_current_month: 2015-04-01
last_day_of_previous_month: 2015-03-31
date_from : 2013-04-01
date_thru : 2015-03-31
So you should be able to say something like this:
select *
from foo t
where t.transaction_date between dateadd(year,-2, dateadd(day,1-day(#today),#today) )
and dateadd(day, -day(#today),#today)
If you have to deal with datetime values rather than date, its easier to not use between and say something like this:
declare #today date = current_timestamp -- get the current date without a time component
select *
from foo t
where t.transaction_date >= dateadd(year,-2, dateadd(day,1-day(#today),#today) )
and t.transaction_date < dateadd(year, 0, dateadd(day, -day(#today),#today)
[superfluous addition of 0 years added for clarity]

Unexpected behaviour of DATEPART

Script that lists dates and week number of date:
DECLARE #initDate DATE = '2014-01-01';
DECLARE #endDate DATE = '2014-12-31';
WITH dates (date, week)
AS (SELECT #initDate date,
Datepart(ww, #initDate) week
UNION ALL
SELECT Dateadd(ww, 1, t.date),
Datepart(ww, t.date)
FROM dates t
WHERE t.date < #endDate)
SELECT dates.date,
dates.week
FROM dates
The first three rows are:
date: week number
---------- -----------
2014-01-01 1
2014-01-08 1
2014-01-15 2
..... ..
..... ..
I guess it should be 2 for the second row and 3 for the first, isn't it?
Is it some kind of bug in DATEPART? Even if something is depends on first day of the year, first row date differs from second on one week with any settings.
Could you clarify this, please?
It is because of Recursive CTE not because of Datepart.
In the Recursive part of CTE week is still holding the previous week not the new date that generated in recursive part
Try changing your query like this.
DECLARE #initDate DATE = '2014-01-01';
DECLARE #endDate DATE = '2014-12-31';
WITH dates (date, week)
AS (SELECT #initDate date,
Datepart(ww, #initDate) week
UNION ALL
SELECT Dateadd(ww, 1, t.date),
Datepart(ww, Dateadd(ww, 1, t.date))
FROM dates t
WHERE t.date < #endDate)
SELECT dates.date,
dates.week
FROM dates

Comparing dates in SQL Server

I have a DateTime column named EXP_Date which contains date like this :
2014-07-13 00:00:00.000
I want to compare them, like this query :
SELECT COUNT(*)
FROM DB
WHERE ('2014-07-15' - EXP_DATE) > 1
I expect to see the number of customers who have their services expired for over a month.
I know this query wouldn't give me the correct answer, the best way was if I separate the Year / Month / Day into three columns, but isn't any other way to compare them as they are?
You can use DATEADD
SELECT COUNT(*)
FROM DB
where EXP_DATE < DATEADD(month, -1, GETDATE())
Try this
SELECT COUNT(*)
FROM DB
where DATEADD(month, -1, GETDATE()) > EXP_DATE
SELECT COUNT(EXPIRE)FROM
(Select CASE WHEN EXP_DATE < DATEADD(month, -1, GETDATE())THEN 1 ELSE 0 END)AS EXPIRE FROM DB
)tt
Another way using DATEDIFF
SET DATEFORMAT DMY --I like to use "dateformat"
SELECT COUNT(*)
FROM DB
WHERE (DATEDIFF(DAY,#EXP_DATE,GETDATE())) >= 30 --Remember, instead "Day" you can use week, month, year, etc
Syntax: DATEDIFF ( datepart , startdate , enddate )
Depart: year, quarter, month, day, week...
For more information you can visit MSDN

Number of days in quarter, SQL Server

I want to calculate the number of days per-quarter if start date and finish dates are given.
for example, one table has two columns, start date and finish date.
start date = 1st september and finish is 14th november.
I want to calculate the number of days present in between these two days that are present in each quarter
Q3 - 30 days
Q4 - 45 days (for this scenario)
Regards.
declare #StartDate date='2012-09-01';
declare #EndDate date='2012-11-14';
select CEILING(month(dateadd(q,datediff(q,0,dateadd(dd,number ,#StartDate)),0))/3.0) as QuarterNo,
COUNT(*) as 'number of days'
from master..spt_values
where type='p'
and dateadd(dd,number ,#StartDate)<=#EndDate
group by dateadd(q,datediff(q,0,dateadd(dd,number ,#StartDate)),0)
SQL fiddle demo
You can use a recursive query to get this. This generates the list of dates between your start and end date and then gets the count of days per quarter:
;with cte (start, enddate) as
(
select startdate, enddate
from yourtable
union all
select dateadd(dd, 1, start), enddate
from cte
where dateadd(dd, 1, start) <= enddate
)
select datepart(q, start) Quarter, count(datepart(q, start)) NoDays
from cte
group by datepart(q, start)
See SQL Fiddle with Demo