How do I fetch data grouped by month and year including the period with no data? - sql

Relatively new to SQL and I am stumped on this little issue. This doesn't seem to be very difficult to do, but I just can't seem to figure it out.
I am trying to get a count of transactions from a table, but I can't seem to get sql to get me to show all of the months instead of only the months and the year that the transactions occured in.
Here is the query:
SELECT YEAR(dbo.countproject.trans_date) AS [TransYear]
, MONTH (dbo.countproject.trans_date) AS [TransMonth]
, COUNT(Id) AS TransNum
FROM dbo.countproject
WHERE dbo.countproject.make_name = 'Honda'
AND dbo.countproject.model_name = 'Civic'
AND dbo.countproject.type = 'Sale'
AND dbo.countproject.trans_type LIKE '%%EU'
AND dbo.countproject.mfr = '2000'
GROUP BY YEAR(dbo.countproject.trans_date)
, MONTH(dbo.countproject.trans_date)
ORDER BY YEAR(dbo.countproject.trans_date)
The query returns the following result set:
| TransYear | TransMonth | TransNum |
|-----------|------------|----------|
| 2004 | 1 | 5 |
| 2004 | 3 | 1 |
| 2005 | 4 | 2 |
and so forth....
I am trying to get it to show all the months and years even if the value is NULL.
I tried creating a new table which will have the year and the month as columns to get it to join somehow, but I am lost.
Any help would be appreciated.

If you are using SQL Server 2005 or above, you could use Common Table Expressions (CTE) to get the desired result. Below example shows how you can fetch the results as you had described in the question.
Click here to view the demo in SQL Fiddle.
Description:
Create and insert statements create the table and populates with some sample data. I have created the table based on the query provided in the question.
The statement within the WITH clause is executing a recursive expression. In this case the SELECT above the UNION ALL fetches the minimum and maximum dates available in the table dbo.countproject
Once the minimum date is fetched, the second SELECT statement after the UNION ALL increments the date in 1 month intervals until the recursive expression reaches the maximum date available in the table.
The recursive CTE has produced all the available dates possible. This output is available in the table named alltransactions.
We have to join this CTE output alltransactions with the actual table countproject using LEFT OUTER JOIN since we want to show all years and months even if there are no transactions.
The tables alltransactions and countproject are joined on the year and month parts of the date. The query then applies the necessary filters in the WHERE clause and then groups the data by year and month before ordering it by year and month.
You can notice from the sample data that the earliest date in the table is 2004-07-01 and the latest date is 2005-12-01. Hence the output shows from year 2004 / month 07 till year 2005 / month 12.
Hope that helps.
Script:
CREATE TABLE dbo.countproject
(
id INT NOT NULL IDENTITY
, trans_date DATETIME NOT NULL
, make_name VARCHAR(20) NOT NULL
, model_name VARCHAR(20) NOT NULL
, type VARCHAR(20) NOT NULL
, trans_type VARCHAR(20) NOT NULL
, mfr INT NOT NULL
);
INSERT INTO dbo.countproject (trans_date, make_name, model_name, type, trans_type, mfr) VALUES
('1900-01-01', 'Honda', 'Civic', 'Sale', 'EU', 2000),
('1900-01-01', 'Toyota', 'Corolla', 'Sale', 'EU', 2000),
('2004-07-01', 'Nissan', 'Altima', 'Sale', 'EU', 2000),
('2005-12-01', 'Toyota', 'Camry', 'Sale', 'EU', 2000),
('2004-04-01', 'Ford', 'Focus', 'Sale', 'EU', 2000),
('2005-08-01', 'Honda', 'Civic', 'Sale', 'EU', 2000),
('2005-11-01', 'Toyota', 'Camry', 'Sale', 'EU', 2000),
('2004-08-01', 'Toyota', 'Corolla', 'Sale', 'EU', 2000),
('2005-12-01', 'Honda', 'Civic', 'Sale', 'EU', 2000),
('2004-07-01', 'Honda', 'Civic', 'Sale', 'EU', 2000),
('2004-11-01', 'Honda', 'Civic', 'Sale', 'EU', 2000),
('2005-08-01', 'Honda', 'Civic', 'Sale', 'EU', 2000);
;WITH alltransactions
AS
(
SELECT MIN(trans_date) AS continuousdate
, MAX(trans_date) AS maximumdate
FROM dbo.countproject
WHERE trans_date <> '1900-01-01'
UNION ALL
SELECT DATEADD(MONTH, 1, continuousdate) AS continuousdate
, maximumdate
FROM alltransactions
WHERE DATEADD(MONTH, 1, continuousdate) <= maximumdate
)
SELECT YEAR(at.continuousdate) AS [Year]
, MONTH(at.continuousdate) AS [Month]
, COUNT(cp.trans_date) AS [Count]
FROM alltransactions at
LEFT OUTER JOIN countproject cp
ON YEAR(at.continuousdate) = YEAR(cp.trans_date)
AND MONTH(at.continuousdate) = MONTH(cp.trans_date)
AND cp.make_name = 'Honda'
and cp.model_name = 'Civic'
and cp.type = 'Sale'
and cp.trans_type LIKE '%EU'
and cp.mfr = '2000'
GROUP BY YEAR(at.continuousdate)
, MONTH(at.continuousdate)
ORDER BY [Year]
, [Month];
Output:
Year Month Count
----- ------ -----
2004 4 0
2004 5 0
2004 6 0
2004 7 1
2004 8 0
2004 9 0
2004 10 0
2004 11 1
2004 12 0
2005 1 0
2005 2 0
2005 3 0
2005 4 1
2005 5 0
2005 6 0
2005 7 0
2005 8 2
2005 9 0
2005 10 0
2005 11 0
2005 12 1

You have to use an LEFT or RIGHT OUTER JOIN!
Here is an easy sample: http://www.w3schools.com/sql/sql_join_left.asp
You should get it done by yourself.
Greetings

Alas, the SQL statement can only return the data in the table. If you want all months, you need either a table with the year/month combinations you are intererested in or, preferably, a calendar table with all days and information about them.
With a calendar table, your query have a from clause that looked like:
from
(
select distinct year(date) as yr, month(date) as mon
from calendar c
where date between <earliest> and <latest>
) c
left outer join CountTable ct
on c.yr = year(ct.trans_date)
and c.mon = month(ct.trans_date)

Related

How to select a specific date or a max date in PostgreSQL

Greetings I need to select a specific date and if the specific date does not match I need to select the max, I'm no expert in SQL this is what I achieved so far but is returning all records:
select
scs.subscription_id,
case
when scs.end_date = max(scs.end_date) then max(scs.end_date)
when scs.end_date = '1900-01-01 00:00:00.000Z' then '1900-01-01 00:00:00.000Z'
end as end_date
from
sim_cards sc
inner join sim_card_subscriptions scs on
sc.id = scs.sim_card_id
where
scs.subscription_id = 1
group by
scs.end_date,
scs.subscription_id
Assuming you want that (match a date X or use MAX) policy to be applied for each row individually and the MAX has to be calculated only across related subscriptions (sharing the same sim_card_id) so that you can use max(end_date::date) over(partition by sim_card_id) window function in your CASE statement to fallback to MAX if the specific date is not matched.
Consider the following sample dataset
with data (id, sim_card_id, end_date) as (
values
(1, 1, '2021-08-05 20:21:00'),
(2, 1, '2021-10-10 12:12:10'),
(3, 1, '2021-12-11 00:11:14'),
(4, 2, '2021-12-14 09:08:45'),
(5, 2, '2021-12-14 15:42:07'),
(6, 3, '2021-10-09 20:20:33')
)
select
id,
case
when end_date::date = '2021-10-10' then end_date::date
else max(end_date::date) over(partition by sim_card_id)
end as end_date
from data
which yields the following output:
1 2021-12-11 -- max across sim_card ID=1
2 2021-10-10 -- matches desired date
3 2021-12-11 -- max across sim_card ID=1
4 2021-12-14 -- max across sim_card ID=2
5 2021-12-14 -- max across sim_card ID=2
6 2021-10-09 -- matches desired date

Calculate Date difference between multiple rows SQL

I need to calculate the date difference between multiple rows. The scenario is I have a vehicle that can do inspections throughout the month as well as when the vehicle is assigned to a different project. I want to calculate that how many days that a vehicle is assigned to the project per month or previous month. I have tried multiple ways and I can't get even closer. I am relatively new to stack overflow. Apologies if anything is missing. Please let me know if this can be done. Thank you.
All the columns are in one single table if that helps. Please let me know the query on how to achieve this
I am using SQL server 2017.
Original Data
Expected Output
I am not proud of this solution, but I think it works for you. My approach was to create a table of days and then look at which project the vehicle was assigned to each day. Finally, aggregate by month and year to get the results. I had to do this as a script since you can use aggregate functions in the definitions of recursive CTEs, but you may find a way to do this without needing a recursive CTE.
I created a table variable to import your data so I could write this. Note, I added an extra assignment to test assignments that spanned months.
DECLARE #Vehicles AS TABLE
(
[VehicleID] INT NOT NULL,
[Project] CHAR(2) NOT NULL,
[InspectionDate] DATE NOT NULL
);
INSERT INTO #Vehicles
(
[VehicleID],
[Project],
[InspectionDate]
)
VALUES
(1, 'P1', '2021-08-20'),
(1, 'P1', '2021-09-05'),
(1, 'P2', '2021-09-15'),
(1, 'P3', '2021-09-20'),
(1, 'P2', '2021-10-10'),
(1, 'P1', '2021-10-20'),
(1, 'P3', '2021-10-21'),
(1, 'P2', '2021-10-22'),
(1, 'P4', '2021-11-15'),
(1, 'P4', '2021-11-25'),
(1, 'P4', '2021-11-30'),
(1, 'P1', '2022-02-05');
DECLARE #StartDate AS DATE, #EndDate AS DATE;
SELECT #StartDate = MIN([InspectionDate]), #EndDate = MAX([InspectionDate])
FROM #Vehicles;
;WITH [seq]([n])
AS (SELECT 0 AS [n]
UNION ALL
SELECT [n] + 1
FROM [seq]
WHERE [n] < DATEDIFF(DAY, #StartDate, #EndDate)),
[days]
AS (SELECT DATEADD(DAY, [n], #StartDate) AS [d]
FROM [seq]),
[inspections]
AS (SELECT [VehicleID],
[Project],
[InspectionDate],
LEAD([InspectionDate], 1) OVER (PARTITION BY [VehicleID]
ORDER BY [InspectionDate]
) AS [NextInspectionDate]
FROM #Vehicles),
[assignmentsByDay]
AS (SELECT [d].[d], [i].[VehicleID], [i].[Project]
FROM [days] AS [d]
INNER JOIN [inspections] AS [i]
ON [d].[d] >= [i].[InspectionDate]
AND [d] < [i].[NextInspectionDate])
SELECT [assignmentsByDay].[VehicleID],
[assignmentsByDay].[Project],
MONTH([assignmentsByDay].[d]) AS [month],
YEAR([assignmentsByDay].[d]) AS [year],
COUNT(*) AS [daysAssigned]
FROM [assignmentsByDay]
GROUP BY [assignmentsByDay].[VehicleID],
[assignmentsByDay].[Project],
MONTH([assignmentsByDay].[d]),
YEAR([assignmentsByDay].[d])
ORDER BY [year], [month], [assignmentsByDay].[VehicleID], [assignmentsByDay].[Project]
OPTION(MAXRECURSION 0);
And the output is:
VehicleID
Project
month
year
daysAssigned
1
P1
8
2021
12
1
P1
9
2021
14
1
P2
9
2021
5
1
P3
9
2021
11
1
P1
10
2021
1
1
P2
10
2021
20
1
P3
10
2021
10
1
P2
11
2021
14
1
P4
11
2021
16
1
P4
12
2021
31
1
P4
1
2022
31
1
P4
2
2022
4
I think you are looking for this:
select vehicleId
, Project
, month(inspectiondate) month
, year(inspectiondate) year
, datediff(day , min(inspectiondate), case when max(inspectiondate) = min(inspectiondate) then eomonth(min(inspectiondate)) else max(inspectiondate) end) days
from Vehicles
group by vehicleId, Project , month(inspectiondate), year(inspectiondate)
This query in for each month/year for each specific vehicle in a project in that month/year , you get the max and min inspection date and calculate the difference.
db<>fiddle here

Count records using Date filter related tables SQL Server

I'm trying to calculates the number of reports (report_user table) per user between two date (Calendar table) and by day worked (agenda_user table).
Here is the diagram of my tables:
Calendar table :
DATE Year Month
---------------------------------
2020-01-01 2020 1
2020-01-02 2020 1
2020-01-03 2020 1
2020-01-04 2020 1
AGENDA_USER table :
ID_USER DATE Value
---------------------------------
1 2020-01-01 1
2 2020-01-01 1
1 2020-01-02 0
2 2020-01-02 1
User table :
ID_USER Name
-------------------------
1 Jack
2 Robert
Report_Result table :
ID_USER Date Result
-----------------------------------
1 2020-01-01 good
1 2020-01-01 good
2 2020-01-01 bad
2 2020-01-01 good
2 2020-01-02 good
2 2020-01-02 good
Result I'm trying to find with an SQL query
ID_USER Date Number of report Day work report/work
---------------------------------------------------------------------------
1 2020-01-01 2 1 2/1 = 2
2 2020-01-01 2 1 1
1 2020-01-02 0 0 0
2 2020-01-02 2 1 2
SELECT
REPORT_USER.ID_USER,
COUNT(ID_USER) AS result
FROM [DB].[dbo].REPORT_USER AS report,
JOIN [DB].[dbo].[USER] AS [USER]
ON [USER].ID_USER = report.ID_USER
JOIN [DB].[dbo].AGENDA_USER AS agenda
ON agenda.ID_USER = report.ID_USER
WHERE CAST(agenda.[Date] AS DATE) >= '2020-09-01'
AND CAST(agenda.[Date] AS DATE) <= '2021-07-28'
AND [USER].ID_user = 1167
GROUP BY
report.ID_VENDEUR;
I'm not entirely sure I understand your problem, but I think I'm reasonably close so here is a start, point out my invalid assumptions and we can refine. More data, particularly in Agenda and Reports would really help. An explanation is below (plus see the comment in the code).
The overall flow is to generate a list of days/people you want to report on (cteUserDays), generate a list of how many reports each user generated on each date (cteReps), generate a list of who worked on what days (cteWork), and then JOIN all 3 parts together using a LEFT OUTER JOIN so the report covers all workers on all days.
EDIT: Add cteRepRaw where DATETIME is converted to DATE and "bad" reports are filtered out. Grouping and counting still happens in cteReps, but joining to cteUserDays is not there because it was adding 1 to count if there was a NULL.
DECLARE #Cal TABLE (CalDate DATETIME, CalYear int, CalMonth int)
DECLARE #Agenda TABLE (UserID int, CalDate DATE, AgendaVal int)
DECLARE #User TABLE (UserID int, UserName nvarchar(50))
DECLARE #Reps TABLE (UserID int, CalDate DATETIME, RepResult nvarchar(50))
INSERT INTO #Cal(CalDate, CalYear, CalMonth)
VALUES ('2020-01-01', 2020, 1), ('2020-01-02', 2020, 1), ('2020-01-03', 2020, 1), ('2020-01-04', 2020, 1)
INSERT INTO #Agenda(UserID, CalDate, AgendaVal)
VALUES (1, '2020-01-01', 1), (2, '2020-01-01', 1), (1, '2020-01-02', 0), (2, '2020-01-02', 1)
INSERT INTO #User (UserID , UserName )
VALUES (1, 'Jack'), (2, 'Robert')
INSERT INTO #Reps (UserID , CalDate , RepResult )
VALUES (1, '2020-01-01', 'good'), (1, '2020-01-01', 'good')
, (2, '2020-01-01', 'bad'), (2, '2020-01-01', 'good')
, (2, '2020-01-02', 'good'), (2, '2020-01-02', 'good')
; with cteUserDays as (
--First, you want zeros in your table where no reports are, so build a table for that
SELECT CONVERT(DATE, D.CalDate) as CalDate --EDIT add CONVERT here
, U.UserID FROM #Cal as D CROSS JOIN #User as U
WHERE D.CalDate >= '2020-01-01' AND D.CalDate <= '2021-07-28'
--EDIT Watch the <= date here, a DATE is < DATETIME with hours of the same day
), cteRepRaw as (--EDIT Add this CTE to convert DATETIME to DATE so we can group on it
--Map the DateTime to a DATE type so we can group reports from any time of day
SELECT R.UserID
, CONVERT(DATE, R.CalDate) as CalDate --EDIT add CONVERT here
, R.RepResult
FROM #Reps as R
WHERE R.RepResult='good' --EDIT Add this test to only count good ones
), cteReps as (
--Get the sum of all reports for a given user on a given day, though some might be missing (fill 0)
SELECT R.UserID , R.CalDate , COUNT(*) as Reports --SUM(COALESCE(R.RepResult, 0)) as Reports
FROM cteRepRaw as R--cteUserDays as D
--Some days may have no reports for that worker, so use a LEFT OUTER JOIN
--LEFT OUTER JOIN cteRepRaw as R on D.CalDate = R.CalDate AND D.UserID = R.UserID
GROUP BY R.UserID , R.CalDate
) , cteWork as (
--Unclear what values "value" in Agenda can take, but assuming it's some kind of work
-- unit, like "hours worked" or "shifts" so add them up
SELECT A.UserID , A.CalDate, SUM(A.AgendaVal) as DayWork FROM #Agenda as A
WHERE A.CalDate >= '2020-01-01' AND A.CalDate <= '2021-07-28'
GROUP BY A.CalDate, A.UserID
)
SELECT D.UserID , D.CalDate, COALESCE(R.Reports, 0) as Reports, W.DayWork
--NOTE: While it's probably a mistake to credit a report to a day a worker had
--no shifts, it could happen and would throw an error so check
, CASE WHEN W.DayWork > 0 THEN R.Reports / W.DayWork ELSE 0 END as RepPerWork
FROM cteUserDays as D
LEFT OUTER JOIN cteReps as R on D.CalDate=R.CalDate AND R.UserID = D.UserID
LEFT OUTER JOIN cteWork as W on D.UserID = W.UserID AND D.CalDate = W.CalDate
ORDER BY CalDate , UserID
First, as per the comments in your OP "Agenda" represents when the user is working, you don't say how it's structured so I'll assume it can have multiple entries for a given person on a given day (i.e. a 4 hour shift and an 8 hour shift) so I'll add them up to get total work (cteWork). I also assume that if somebody didn't work, you can't have a report for them. I check for this, but normally I'd expect your data validator to pre-screen those out.
Second, I'll assume reports are 1 per record, and a given user can have multiple per day. You have that in your given, but it's important to this solution so I'm restating in case somebody else reads this later.
Third, I assume you want all days reported for all users, I assure this by generating a CROSS join between users and days (cteUserDays)

Returning Records by Week

I'm trying to come up with a way to return a result set from the below data without a loop that shows the number of records by Team for a particular date range by week.
I've got a Date table (https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/) that has every day/week/year referenced, but not sure how to connect it up.
Create Table #Team
(
TeamID int,
TeamName varchar(20)
)
insert into #Team
(
TeamID,
TeamName
)
select
1,
'Team 1'
union all
select
2,
'Team 2'
union all
select
3,
'Team 3'
union all
select
4,
'Team 4'
Create Table #Entries
(
EntryID int,
DateCreated datetime,
TeamID int
)
insert into #Entries
(
EntryID,
DateCreated,
TeamID
)
select
1,
'2 Nov 2020',
1
union all
select
2,
'4 Nov 2020',
2
I've got this query:
select
T.TeamName,
WeekOfYear,
WeekOfMonth,
count(*) as Count(*)
from
#Team T
Left Join #Entries E on
T.TeamID = E.TeamID
Inner Join DimDate D on
cast(E.DateCreated as date) = D.[Date]
group by
T.TeamName,
WeekOfYear,
WeekOfMonth
Where it fails is:
It doesn't include the teams with 0 results
I need to be able to show results for multiple weeks through a date range. In the above example, they would be 0.
I think the trick is to first generate all the rows you need, then LEFT JOIN those onto their results to get what you want.
Note that in your query, you are pulling out WeekOfYear and WeekOfMonth, but you probably also want to pull out Year in case the data crosses years or goes for multiple years.
For the date range
I have two variables #RangeStart and #RangeEnd- both dates - to do filtering
I create a table (probably incorrect) to model the date dimension table
CREATE TABLE #DimDate ([Date] date, WeekOfYear int, WeekOfMonth int, y_year int)
INSERT INTO #DimDate ([Date], WeekOfYear, WeekOfMonth, y_year) VALUES
('20201029', 35, 4, 2020),
('20201030', 35, 4, 2020),
('20201031', 35, 4, 2020),
('20201101', 36, 1, 2020),
('20201102', 36, 1, 2020),
('20201103', 36, 1, 2020),
('20201104', 36, 1, 2020);
-- Note that I called the year 'y_year' - will need to be changed
-- to your value (or converted to YEAR([date]) function)
DECLARE #RangeStart date = '20201030';
DECLARE #RangeEnd date = '20201102';
WITH AllTeamDates AS
(SELECT T.TeamId,
D.[Date],
D.WeekOfMonth,
D.WeekOfYear,
D.y_year
FROM #Team T
CROSS JOIN #DimDate D
WHERE D.[Date] BETWEEN #RangeStart AND #RangeEnd
)
SELECT ATD.y_year,
ATD.WeekOfYear,
ATD.WeekOfMonth,
ATD.TeamID,
COUNT(E.EntryID) AS NumEntries
FROM AllTeamDates ATD
LEFT OUTER JOIN #Entries E
ON ATD.TeamID = E.TeamID AND ATD.Date = E.DateCreated
GROUP BY ATD.y_year,
ATD.WeekOfYear,
ATD.WeekOfMonth,
ATD.TeamID;
Results for the above, with your data and my date table and range dates applied (noting that the date range I selected gets the first value in #Entries for 2 Nov, but doesn't get the second for 4 Nov).
y_year WeekOfYear WeekOfMonth TeamID NumEntries
2020 35 4 1 0
2020 35 4 2 0
2020 35 4 3 0
2020 35 4 4 0
2020 36 1 1 1
2020 36 1 2 0
2020 36 1 3 0
2020 36 1 4 0
Note that in this case I am creating all possible dates, then grouping to get week-by-week at the very end. It is possible to also do this by grouping into week-by-week data as soon as possible (e.g., the CTE will return data by week instead of day, then the outer part of the LEFT JOIN also then needs to be grouped into weeks first).
WITH AllTeamWeeks AS
(SELECT T.TeamId, D.WeekOfMonth, D.WeekOfYear, D.y_year
FROM #Team T
CROSS JOIN #DimDate D
WHERE D.[Date] BETWEEN #RangeStart AND #RangeEnd
GROUP BY T.TeamId, D.WeekOfMonth, D.WeekOfYear, D.y_year
),
AllEntries AS
(SELECT E.TeamId, D.WeekOfMonth, D.WeekOfYear, D.y_year,
COUNT(E.EntryID) AS NumEntries
FROM #Entries E
INNER JOIN #DimDate D ON E.DateCreated = D.Date
WHERE E.[DateCreated] BETWEEN #RangeStart AND #RangeEnd
GROUP BY E.TeamId, D.WeekOfMonth, D.WeekOfYear, D.y_year
)
SELECT ATW.y_year,
ATW.WeekOfYear,
ATW.WeekOfMonth,
ATW.TeamID,
ISNULL(AE.NumEntries,0) AS NumEntries
FROM AllTeamWeeks ATW
LEFT OUTER JOIN AllEntries AE
ON ATW.TeamID = AE.TeamID
AND ATW.WeekOfMonth = AE.WeekOfMonth
AND ATW.WeekOfYear = AE.WeekOfYear
AND ATW.y_year = AE.y_year;
This gives the same results, and possibly provides a performance benefit, but is more complex and you'd probably need to ensure that SQL Server is getting accurate estimates/etc when doing the multiple GROUP BYs.
As such I wouldn't use it unless there is a performance issue with the first one - and if there was, I'd also try turning the CTE into a temporary table first, then joining that to #Entries.

Dates by quarter

I would like to count number of birthdays by quarters in SQL Server
i.e. Between Jan 1 - March 31, April 1 - June 30, July 1 - Sep 30 & Oct 1 - Dec 31.
Please help me with the Date function in Sql Server to do this.
I pass BDate as DATETIME . I want to use this BDate to populate 4 fields of type int with counts for number of birthdays in each quarter.
Thanks.
As long as your "BDate" column is datetime, you can achieve this quite easily using this query:
SELECT DATEPART(QUARTER,BDATE), COUNT(*)
FROM TABLE1
GROUP BY DATEPART(QUARTER,BDATE)
ORDER BY DATEPART(QUARTER,BDATE) ASC
Here's a working fiddle using some random data: http://sqlfiddle.com/#!6/7734b/1
Data set up:
create table test (
d1 varchar(10),
d2 datetime
);
insert into test (d1,d2) values ('2015-01-28','2015-01-28');
insert into test (d1,d2) values ('2015-02-13','2015-02-13');
insert into test (d1,d2) values ('2015-07-19','2015-07-19');
insert into test (d1,d2) values ('2015-11-04','2015-11-04');
If you just want to get counts for each of the quarters present in your data:
select DATEPART(QUARTER, d2), count(*)
from test
group by DATEPART(QUARTER, d2);
You can use d1 or d2 (SQL Server will handle the varchar or datetime properly).
If you want to include all four quarters, even if they're not present in your data:
select qs.q, count(t.d2) as c
from (
SELECT 1 as q
UNION ALL
SELECT 2 as q
UNION ALL
SELECT 3 as q
UNION ALL
SELECT 4 as q) qs
left join test t
on qs.q = DATEPART(QUARTER, t.d2)
group by qs.q;
Again, you can use either d1 or d2, it doesn't matter. The "qs" query just gets the numbers 1 to 4, then that is outer joined to the table with the birth dates.
Please try this:
--replace '19000102' with your int column
select SUM(CASE WHEN DATEPART(MONTH,CONVERT (datetime,convert(char(8),19000102)))
IN (1,2,3)
THEN 1
ELSE 0
END) as 'Q1',
SUM(CASE WHEN DATEPART(MONTH,CONVERT (datetime,convert(char(8),19000102)))
IN (4,5,6)
THEN 1
ELSE 0
END) as 'Q2',
SUM(CASE WHEN DATEPART(MONTH,CONVERT (datetime,convert(char(8),19000102)))
IN (7,8,9)
THEN 1
ELSE 0
END) as 'Q3',
SUM(CASE WHEN DATEPART(MONTH,CONVERT (datetime,convert(char(8),19000102)))
IN (10,11,12)
THEN 1
ELSE 0
END) as 'Q4'