How to group by month and year for entire calender year until this month in SQL - sql

I have this query
DECLARE #DATE datetime
SELECT #Date = '2014-04-01'
SELECT #Date, COUNT(*) FROM Claim C
INNER JOIN Prop_Vehicles PV ON PV.Prop = C.Prop
WHERE PV.Vehicle IN (1,2) AND
C.DateCreate >= #DATE AND
ClaimCodeId =5
I want to group by month wise for the calnder year. For example
April 2014 - 200
May 2014 - 300
June 2014 - 500
.
.
october 2014 - 100
something like this. How to achieve it? Could someone help me how to split #Date into two fields and also group by month year wise until current month like I mentioned above?
I reckon datepart function would do? Let me also check that.
Thank you in advance.

In case some months don't have data then this would skip those months.
If you want all months data even if value is zero, then you need to construct months table and join with it
SELECT DATEADD(MONTH, DATEDIFF(MONTH,0,C.DateCreate), 0), COUNT(*) FROM Claim C
INNER JOIN Prop_Vehicles PV ON PV.Prop = C.Prop
and PV.Vehicle IN (1,2) AND
and C.DateCreate >= #DATE AND
AND ClaimCodeId =5
group by DATEADD(MONTH, DATEDIFF(MONTH,0,C.DateCreate), 0)
as per latest comment
here is the way to get all months data and also to display year and month
DECLARE #DATE datetime
SELECT #Date = '2014-04-01'
;with cte
as
(
select DATEADD(month, datediff(month,0,#Date), 0) as monthVal,1 as N
union all
select DATEADD(month, datediff(month,0,#Date)+N, 0) as monthVal, N+1
FROM cte
WHERE n <=5
)
SELECT DATENAME(year, monthval) as Year, datename(month,monthVal) as Month, COUNT(*) FROM
cte
left join Claim C
on DATEADD(month, datediff(month,0,C.DAteCreate)= cte.monthVal
INNER JOIN Prop_Vehicles PV ON PV.Prop = C.Prop
and PV.Vehicle IN (1,2) AND
and C.DateCreate >= #DATE AND
AND ClaimCodeId =5
group by DATENAME(year, monthval) , datename(month,monthVal)

Related

SQL Query to get oldest date

I'm new to SQL and have a large database that contains IDs and Service Dates and I need to write a query to give me the first date each ID had a service.
I tried:
SELECT dbo.table.ID, dbo.otherTable.ServiceDate AS EasliestDate
FROM dbo.table INNER JOIN dbo.table.ID = dbo.otherTable.ID
But the output is every service for every ID, which has too many results to sort through. I want the output to only show the ID and the oldest service date. Any advice is appreciated.
EDIT: To be more precise, the output I am looking for is the ID and service date if the oldest service date is during the year that I specify. I.E. if ID = 1 has a service in 2015 and 2016 and I am searching for IDs in 2016 then ID = 1 should not appear in the results because there was an earlier service in 2015.
EDIT: Thanks everyone who helped with this! The answer I accepted did exactly what I asked. Major kudos to Patty though who who elaborated on how to further filter the outcome by year.
Use GROUP BY and MIN to get the first date for each ID:
SELECT dbo.table.ID,
MIN(dbo.otherTable.ServiceDate) AS EasliestDate
FROM dbo.table
INNER JOIN otherTable
ON dbo.table.ID = dbo.otherTable.ID
GROUP BY dbo.table.ID;
ADDENDUM
In reference to a question in the comments:
how would I also restrict it to show only those who had a service in a specific year?
It would depend on your exact requirements, consider the following set:
ID ServiceDate
--------------------
1 2014-05-01
1 2015-08-01
1 2016-07-07
2 2015-08-19
You would only want to include ID = 1 if the year you specified was 2016, but assuming you still wanted to return the first date of 2014-05-01 then you would need to add a having clause with a case statement to get this.
DECLARE #Year INT = 2016;
DECLARE #YearStart DATE = DATEADD(YEAR, #Year - 1900, '19000101'),
#YearEnd DATE = DATEADD(YEAR, #Year - 1900 + 1, '19000101');
SELECT #YearStart, #YearEnd
SELECT t.ID,
MIN(o.ServiceDate) AS EasliestDate
FROM dbo.table AS t
INNER JOIN otherTable AS o
ON o.ID = r.ID
GROUP BY t.ID
HAVING COUNT(CASE WHEN o.ServiceDate >= #YearStart
AND o.ServiceDate < #YearEnd THEN 1 END) > 0;
If you only want the earliest date in 2016 the a where clause would suffice
DECLARE #Year INT = 2016;
DECLARE #YearStart DATE = DATEADD(YEAR, #Year - 1900, '19000101'),
#YearEnd DATE = DATEADD(YEAR, #Year - 1900 + 1, '19000101');
SELECT #YearStart, #YearEnd
SELECT t.ID,
MIN(o.ServiceDate) AS EasliestDate
FROM dbo.table AS t
INNER JOIN otherTable AS o
ON o.ID = r.ID
WHERE o.ServiceDate >= #YearStart
AND o.ServiceDate < #YearEnd
GROUP BY t.ID;
It is worth noting there is a very good reason I have chosen to calculate the start of the year, and the start of the next year and used
WHERE o.ServiceDate >= #YearStart
AND o.ServiceDate < #YearEnd
Instead of just
WHERE DATEPART(YEAR, o.ServiceDate) = 2016;
In the former, an index on ServiceDate can be used whereas in the latter, the DATEPART calculation must be done on every record and this can cause significant performace issues.
ADDENDUM 2
To do the following:
The exact thing I want then would be IDs who's earliest service is in the year I specify.
Then you would need a having clause, just a different one to the one I posted before:
DECLARE #Year INT = 2016;
DECLARE #YearStart DATE = DATEADD(YEAR, #Year - 1900, '19000101'),
#YearEnd DATE = DATEADD(YEAR, #Year - 1900 + 1, '19000101');
SELECT #YearStart, #YearEnd
SELECT t.ID,
MIN(o.ServiceDate) AS EasliestDate
FROM dbo.table AS t
INNER JOIN otherTable AS o
ON o.ID = r.ID
GROUP BY t.ID
HAVING MIN(o.ServiceDate) >= #YearStart
AND MIN(o.ServiceDate) < #YearEnd;
ADDENDUM 3
CREATE VIEW dbo.YourView
AS
SELECT dbo.table.ID,
MIN(dbo.otherTable.ServiceDate) AS EasliestDate
FROM dbo.table
INNER JOIN otherTable
ON dbo.table.ID = dbo.otherTable.ID
GROUP BY dbo.table.ID;
Then you can apply your criteria to the view:
SELECT *
FROM dbo.YourView
WHERE EasliestDate >= '2015-01-01'
AND EasliestDate < '2016-01-01';
You have to include a WHERE in your current query:
SELECT dbo.table.ID, dbo.otherTable.ServiceDate AS EasliestDate
FROM dbo.table INNER JOIN dbo.table.ID = dbo.otherTable.ID
WHERE Month(dbo.otherTable.ServiceDate) = 1
Or you can search with Year(dbo.otherTable.ServiceDate) = 2016
Or you can use Day(dbo.otherTable.ServiceDate) = 1
Or an specific date.
use group by and min to get records. Else you can refer http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/ for better understanding.
You need to use a "Group by" statement. Try this:
SELECT dbo.table.ID, Max(dbo.otherTable.ServiceDate) AS LatestDate, Min(dbo.otherTable.ServiceDate as EarliestDate)
FROM dbo.table INNER JOIN dbo.table.ID = dbo.otherTable.ID
group by dbo.table.ID
Use nested statement to get the min date , and then just match based on ID.
select t1.ID from table1 t1 INNER JOIN
(
SELECT ID, MIN(servicedate) MinServiceDate
FROM table2
GROUP BY ID
) t2 ON t1.ID = t2.ID

How to sum sales by months and compare them

I have a table called SOITEM. In that table the column TOTALPRICE has to be summed and result in the total sales by month, where the column with the dates is called DATELASTFULFILLMENT.
I want to compare sales form Jan 2014 with Jan 2015, then Feb 2014 with Feb 2015 and so forth.
I got this so far, but I'm not sure how continue.
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2014-01-31' AND DATELASTFULFILLMENT<='2014-01-31'
but it only results in totals from Jan 2014....
Thank you.
You could consider grouping your results using the Month/Year from your date field and then using calculating the SUM() for each of those groups :
SELECT DATEPART(Year, DATELASTFULFILLMENT) AS [Year],
DATEPART(Month, DATELASTFULFILLMENT) AS [Month],
SUM(TOTALPRICE) AS Total
FROM SOITEM
GROUP BY DATEPART(Year, DATELASTFULFILLMENT), DATEPART(Month, DATELASTFULFILLMENT)
ORDER BY [Year], [Month]
You can see an interactive example of this here and results demonstrated below :
This works for MySQL. I assume should be the same for MS SQL.
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2014-01-31' AND DATELASTFULFILLMENT<='2014-01-31'
UNION
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2015-01-31' AND DATELASTFULFILLMENT<='2015-01-31'
UNION
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2014-02-31' AND DATELASTFULFILLMENT<='2014-02-31'
UNION
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2014-02-31' AND DATELASTFULFILLMENT<='2015-02-31'
Use a nested query within your select statement -- notice the subtraction from the YEAR within the nested query to pull back the previous year's summary:
SELECT MONTH(so2.DATELASTFULFILLMENT) AS MonthFulfilled,
YEAR(so2.DATELASTFULFILLMENT) AS YearFulfilled,
SUM(so2.TOTALPRICE),
(SELECT SUM(SOITEM.TOTALPRICE) FROM SOITEM WHERE MONTH(DATELASTFULFILLMENT) = MONTH(so2.DATELASTFULFILLMENT) AND YEAR(DATELASTFULFILLMENT) = (YEAR(so2.DATELASTFULFILLMENT)-1)) AS LastYearTotal
FROM SOITEM AS so2
GROUP BY MONTH(so2.DATELASTFULFILLMENT), YEAR(so2.DATELASTFULFILLMENT)
Or you could do it like this.
WITH MonthTotal AS
(
SELECT
DATEADD(MONTH, DATEDIFF(MONTH, 0, DATELASTFULFILLMENT), 0) AS MonthDate
, SUM(TOTALPRICE) AS Total
FROM
SOITEM
GROUP BY
DATEADD(MONTH, DATEDIFF(MONTH, 0, DATELASTFULFILLMENT), 0)
)
SELECT
MonthTotal.MonthDate
, MonthTotal.Total
, PreviousYear.Total AS PreviousYearTotal
FROM
MonthTotal
LEFT JOIN MonthTotal AS PreviousYear
ON DATEADD(YEAR, -1, MonthTotal.MonthDate) = PreviousYear.MonthDatee) = PreviousYear.MonthDate
You first group the results based on themonth date, the calculation converts a date to the 1st of the month the date drops in. We then use these results and join back to it getting last years result as well.

get last 3 month on year in sql server

I want to get last 3 months name from current month. For example current month is December. So, I want get like this October, November and December.
This is my query:
SELECT CONVERT(CHAR, DATENAME(MONTH, IssueDate)) AS MonthName, ItemId
FROM dbo.Issue AS Issue
GROUP BY CONVERT(CHAR, DATENAME(MONTH, IssueDate)), ItemId
HAVING (ItemId = 427)
This returns:
But, my need is:
N.B. When December month close and January month open then October auto excluded as like (November, December and January)
this link is my Database only 2 table (size-243 KB with Zip) on the google drive https://goo.gl/S4m0R5
Add a date diff in a where clause to filter to the last 3 months, and then order by the month number at the end:
SELECT CONVERT(CHAR, DATENAME(MONTH, [IssueDate])) AS MonthName, ItemId
FROM [dbo].[Issue] AS Issue
WHERE datediff(m, [IssueDate], getdate()) between 0 and 2
GROUP BY CONVERT(CHAR, DATENAME(MONTH, [IssueDate])), ItemId, MONTH(IssueDate)
HAVING (ItemId= 427)
order by MONTH(IssueDate);
You can use DATEADD function:
WHERE IssueDate >= dateadd( month, -2, dateadd( day, -datepart( day, getdate() ) + 1, cast( getdate() as date ) ) )
That will give you IssueDate >= '2015-10-01' given today.
That will also work with index you have on IssueDate, if you start doing something like DATEADD / DATEDIFF etc. on IssueDate then the index can only be scanned end-to-end because it needs to processs all rows in the table so renders the index significantly less effective.
DECLARE #t TABLE
(
IssueDate DATETIME,
ItemId INT
)
INSERT INTO #t (IssueDate, ItemId)
VALUES
('20160105', 427),
('20151212', 427),
('20151213', 427),
('20151110', 427),
('20151001', 427),
('20150905', 427)
SELECT DATENAME(MONTH, dt)
FROM (
SELECT DISTINCT TOP(3) DATEADD(MONTH, DATEDIFF(MONTH, 0, IssueDate), 0) AS dt
FROM #t
WHERE ItemId = 427
ORDER BY dt DESC
) t
results -
------------------------------
January
December
November
You can use a recursive CTE to get month names for the last 12 months and then limit it to the last 3 month names in the second part of the query:
;WITH months(MonthNumber) AS
(
SELECT 0
UNION ALL
SELECT MonthNumber+1
FROM months
WHERE MonthNumber < 12
)
SELECT DATENAME(MONTH,DATEADD(MONTH,-MonthNumber,GETDATE())) AS [month]
FROM dbo.Issue AS Issue
CROSS JOIN months m
WHERE m.MonthNumber <3
GROUP BY DATENAME(MONTH,DATEADD(MONTH,-MonthNumber,GETDATE())) , ItemId
HAVING (ItemId = 427)

Data appear at least once for every month in the last X month

My problem:
Table: trans_detail:
PhoneNo | Datetime
01234 | 2013-01-05 20:40:10
01245 | 2013-04-02 21:00:13
05678 | 2013-04-16 01:24:07
04567 | 2013-07-23 07:00:00
etc | etc
I want to get all phoneNo that appears at least once for every month in the last X month (X month can be any month between 1-12).
For example: get all phone no. that appears at least once for Every Month in the last 3 months.
I am using SQL Server 2005.
Here is a quick query that comes close to what you want:
select PhoneNo
from trans_detail d
where d.datetime >= dateadd(mm, -#X, getdate())
group by PhoneNo
having count(distinct year(datetime)*12+month(datetime)) = #X
The where clause filters the data to only include rows in the last #X months. the having clause checks that each month is in the data, by counting the number of distinct months.
The above version of the query assumes that you mean calendar months. So, it has boundary condition problems. If you run it on June 16th, then it looks back one month and makes sure that the phone number appears at least once since May 16th. I am unclear on whether you want to insist that the number appear twice (once in May and once in June) or if once (once during the time period). The solution to this is to move the current date back to the end of the previous month:
select PhoneNo
from trans_detail d cross join
(select cast(getdate() - day(getdate) + 1 as date) as FirstOfMonth const
where d.datetime >= dateadd(mm, -#X, FirstOfMonth) and
d.datetime < FirstOfMonth
group by PhoneNo
having count(distinct year(datetime)*12+month(datetime)) = #X
Here it is. First two CTEs are to find and prepare last X months, third CTE is to group your data by phones and months. At the end just join the two and return where number of matching rows are equal to number of months.
DECLARE #months INT
SET #Months = 3
;WITH CTE_Dates AS
(
SELECT GETDATE() AS Dt
UNION ALL
SELECT DATEADD(MM,-1,Dt) FROM CTE_Dates
WHERE DATEDIFF(MM, Dt,GETDATE()) < #months-1
)
, CTE_Months AS
(
SELECT MONTH(Dt) AS Mn, YEAR(Dt) AS Yr FROM CTE_Dates
)
, CTE_Trans AS
(
SELECT PhoneNo, MONTH([Datetime]) AS Mn, YEAR([Datetime]) AS Yr FROM dbo.trans_detail
GROUP BY PhoneNo, MONTH([Datetime]), YEAR([Datetime])
)
SELECT PhoneNo FROM CTE_Months m
LEFT JOIN CTE_Trans t ON m.Mn = t.Mn AND m.Yr = t.Yr
GROUP BY PhoneNo
HAVING COUNT(*) = #months
SQLFiddle Demo - with added some more data that will match for last 3 months

How to count databases elements in a range of date?

In an SQL Server procedure, I need to get all rows matching some constraints(simple where conditions), and then group them by month.
The goal is to create a graph(in Sql server reporting services), which display all data.
I've already something like this:
Select Count(*) AS Count, Month(a.issueDate) AS Month, Year(a.issueDate) AS Year
FROM MyTable a
WHERE
....
GROUP BY YEAR(a.issueDate), MONTH(a.issueDate)
I got my data, I got my graph, but the problem is that if I've NOT any rows in "MyTable", which match my Where conditions, I won't have any rows.
The result is that I've a graph Starting with january, skipping february, and then displaying march.
I cannot post-process data since it's directly connected to the SQL Server Reporting Services report.
Since I have this problem for ~20 stored procedure, I will appreciate to have the simpliest way of doing it.
Thank you very much for your advices
Let's say you want a specific year:
DECLARE #year INT;
SET #year = 2012;
DECLARE #start SMALLDATETIME;
SET #start = DATEADD(YEAR, #year-1900, 0);
;WITH y AS (SELECT TOP (12) rn = ROW_NUMBER() OVER (ORDER BY [object_id])-1
FROM sys.all_objects ORDER BY [object_id])
SELECT DATEADD(MONTH, y.rn, #start), COUNT(t.issueDate)
FROM y
LEFT OUTER JOIN dbo.MyTable AS t
ON t.issueDate >= DATEADD(MONTH, y.rn, #start)
AND t.issueDate < DATEADD(MONTH, y.rn + 1, #start)
GROUP BY DATEADD(MONTH, y.rn, #start);
If it's not a specific year, then you can do it slightly differently to cover any date range, as long as you provide the 1st day of the 1st month and the 1st day of the last month (or pass 4 integers and construct the dates manually):
DECLARE #startdate SMALLDATETIME, #enddate SMALLDATETIME;
SELECT #startdate = '20111201', #enddate = '20120201';
;WITH y AS (SELECT TOP (DATEDIFF(MONTH, #startdate, #enddate)+1)
rn = ROW_NUMBER() OVER (ORDER BY [object_id])-1
FROM sys.all_objects ORDER BY [object_id]
)
SELECT DATEADD(MONTH, y.rn, #startdate), COUNT(t.issueDate)
FROM y
LEFT OUTER JOIN dbo.MyTable AS t
ON t.issueDate >= DATEADD(MONTH, y.rn, #startdate)
AND t.issueDate < DATEADD(MONTH, y.rn + 1, #startdate)
GROUP BY DATEADD(MONTH, y.rn, #startdate);
In report builder, right click on the date axis, select properties, and then set the axis up as a date range, it will add the empty columns for you, and you won't have to change your SQL
You need to build a table (a Table variable would work best here) that contains all year/month combinations from your minimum to maximum.
You then need to cross join this with your main query to get results for all year/months ready for the graph.