Quickest way to get aggregate date statistics / pivot - sql-server

I am tryig to write what must be a fairly common audit report; number of rows added to a table over time; reported back against previous cycles to understand the trends in the data.
I have a table that audits creation of rows in the database. It has a field RowEnteredDate date time. I am looking to create an audit report Week/ Month/ Current Quarter / Year.
In my head I am looking at this as multiple passes over the data around the dates; which is quite costly in my database. My reasoning at the moment is
I started out with working out the dates for my year / month / quarter
set datefirst 1
declare #dateranges table (
rangelabel varchar(100),
startdate datetime,
enddate datetime,
myrowcount integer identity(1,1)
)
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Year',
DATEADD(yy, DATEDIFF(yy,0,GETDATE()), 0),
DATEADD(ms,-3,DATEADD(yy, DATEDIFF(yy,0,GETDATE() )+1, 0))
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Quarter',
DATEADD(qq, DATEDIFF(qq,0,GETDATE()), 0),
DATEADD(qq, DATEDIFF(qq, - 1, getdate()), - 1)
insert into #dateranges (Rangelabel, startdate, enddate)
select
'Current Month',
DATEADD(month, DATEDIFF(month, 0, getdate()), 0),
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
If my table is tblOfUsefullFacts and my date row is RowEnteredDate what is the best way to get the aggregate; broken by Day.
Date Range Mon Tues Wed Thu Fri Sat Sun
Year To date 12000 13000 12000 3200 98000 8900 4000
Quarter 1 302 407 201 97 1732 120 37
Month ...
I can get the totals by day easily enough using a query like this
select
count(*) ,
datepart(weekday, RowEnteredDate)
from
tblOfUsefullFacts aa
Where
datepart(weekday, RowEnteredDate) is not null
group by datepart(weekday, RowEnteredDate)
order by datepart(weekday, RowEnteredDate) sac
This selects the data out row by row; which i could pivot and loop round to get the data. Im slightly nervous as the real numbers are in the 10's of millions in them and would like to not impact the underlying processing if i can avoid it.
As i need to do this in multiple passes is there a lighter way to do this without running the loops to get the totals? Or a mechanism in SQL my fuzzy brain is ignoring.

This should give you an idea how to do it. Sorry for any syntax errors, it isn't tested.
;with cte as
(
select
d.rangelabel,
datepart(weekday, RowEnteredDate) as WkDay,
count(*) as RowCt
from tblOfUsefullFacts f
join #dateranges d on f.RowEnteredDate between d.startdate and d.enddate
Where datepart(weekday, RowEnteredDate) is not null
group by d.rangelabel,datepart(weekday, RowEnteredDate)
)
select
RangeLabel,
sum(case when WkDay = 1 then RowCt else 0 end) as Sunday,
sum(case when WkDay = 2 then RowCt else 0 end) as Monday,
sum(case when WkDay = 3 then RowCt else 0 end) as Tuesday,
sum(case when WkDay = 4 then RowCt else 0 end) as Wednesday,
sum(case when WkDay = 5 then RowCt else 0 end) as Thursday,
sum(case when WkDay = 6 then RowCt else 0 end) as Friday,
sum(case when WkDay = 7 then RowCt else 0 end) as Saturday
from cte
group by RangeLabel

Related

SQL Tally up value based on the day of the week

Here is what my table looks like
ID | Date
------------
13 2013-03-21 00:00:00
13 2013-03-23 00:00:00
13 2013-03-24 00:00:00
25 2013-03-21 00:00:00
25 2013-03-22 00:00:00
25 2013-03-21 00:00:00
25 2013-03-23 00:00:00
25 2013-03-28 00:00:00
25 2013-03-21 00:00:00
82 2013-03-22 00:00:00
82 2013-03-22 00:00:00
I want it to output (combine sat & sun into 'weekend'). It'll combine all the Mondays, Tuesdays, Wednesdays, etc. and combine the values.
day | total
--------------
Friday 2
Thursday 3
Weekend 3
This is my query so far
SELECT
CASE
WHEN DATENAME(weekday, [date]) = 'Saturday' OR DATENAME(weekday, [date]) = 'Sunday' THEN 'Weekend'
ELSE DATENAME(weekday, [date])
END AS Day,
COUNT(DISTINCT [id]) AS score
FROM [table]
GROUP BY
CASE
WHEN DATENAME(weekday, [date]) = 'Saturday' OR DATENAME(weekday, [date]) = 'Sunday' THEN 'Weekend'
ELSE DATENAME(weekday, [date])
END
Currently I can get my query to output this, but it seems like it isn't adding both thursdays or sat + sun.
Day | total
--------------
Friday 2
Thursday 2
Weekend 2
Appears like what you are actually after is a distinct count of the ID and date combined. Seems like the easiest way, therefore, would be to concatenate the 2 values to make a new unique value and COUNT those DISTINCT values:
SELECT CASE WHEN DATENAME(WEEKDAY, V.[Date]) IN ('Saturday','Sunday') THEN 'Weekend' ELSE DATENAME(WEEKDAY, V.[Date]) END AS [Weekday],
COUNT(DISTINCT CONCAT(ID,[date]))
FROM (VALUES (13, CONVERT(datetime2(0),'2013-03-21T00:00:00')),
(13, CONVERT(datetime2(0),'2013-03-23T00:00:00')),
(13, CONVERT(datetime2(0),'2013-03-24T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-21T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-22T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-21T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-23T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-28T00:00:00')),
(25, CONVERT(datetime2(0),'2013-03-21T00:00:00')),
(82, CONVERT(datetime2(0),'2013-03-22T00:00:00')),
(82, CONVERT(datetime2(0),'2013-03-22T00:00:00'))) V (ID, [Date])
GROUP BY CASE WHEN DATENAME(WEEKDAY, V.[Date]) IN ('Saturday','Sunday') THEN 'Weekend' ELSE DATENAME(WEEKDAY, V.[Date]) END;
Try this!
select count(id)as Total,Day from
(
select id, case when Datename(weekday,dt1) IN('Saturday','Sunday') then 'Weekend' else Datename(weekday,dt1) end as 'Day' from tab
)x
group by Day
Demo here
select Day,Count(day) cnt from (
SELECT
CASE
WHEN DATENAME(weekday, [date]) = 'Saturday' OR DATENAME(weekday, [date]) = 'Sunday' THEN 'Weekend'
ELSE DATENAME(weekday, [date])
END AS Day
,[date]
FROM [Infinite_campus].[dbo].[AttendanceRecord]
) as t
group by Day

Last 2 month record in SQL

I try this SQL query to get current total and also last 2 months records and current month record means total 3 months..
Select distinct
tblRv.Owner,
(Select Count(*) as total from tblvv WHERE MasterID =tblRv.ID and Name <> '')
as currentdata
from tblRe
inner join tblRv
On tblRe.RID = tblRv.RID
WHERE
tblRe.StartDate between dateadd(m, -2, getdate()) and getdate() and
//tblRe.StartDate >= '2016-07-01 00:00:00' AND
//tblRe.EndDate <= '2016-07-08 23:59:59'
and tblRe.Region = 'uk' and
tblRv.Owner='Roh'
order by tblRv.Owner
when i exe this show me like this
OwnerName currentdata
Roh 1
Roh 2
Roh 3
Roh 5
and when i check individually write query and check from date 2016-07-01 and todate 2016-07-30 dates then this show me data 3 and 2016-06-01 00:00:00 and 2016-06-31 23:59:59 show me data 1 and 2016-05-01 00:00:00 ,2016-05-31 23:59:59 show me data 0
so i want data like this
owner july june may
roh 3 1 0
also when there will be current month i.e. aug then data must display last 2 months i.e.june july
Select
tblRv.Owner
,DATENAME(MONTH,tblRe.StartDate) as [Month]
,ISNULL(SUM(total),0) as currentdata
from tblRe
INNER JOIN tblRv ON tblRe.RID = tblRv.RID
LEFT JOIN (
Select Count(*) as total ,MasterID
from tblvv
WHERE Name <> ''
GROUP BY MasterID
) tblvv
ON tblvv.MasterID =tblRv.ID
WHERE tblRe.StartDate >= DATEADD(MONTH, -2, GETDATE())
AND tblRe.EndDate <= GETDATE()
AND tblRe.Region = 'uk'
AND tblRv.[Owner] ='Roh'
GROUP BY tblRv.Owner
,DATENAME(MONTH,tblRe.StartDate)
order by tblRv.[Owner]
You can try doing a pivot query where the three columns to be pivoted are the current, previous, and previous previous month in your data set. Note that I had to rewrite your query to remove the subqueries in the SELECT clause, because this makes it impossible to do aggregation of those columns.
SELECT tblRv.Owner,
SUM(CASE WHEN tblRe.StartDate = GETDATE() THEN t.total ELSE 0 END) AS currMonth,
SUM(CASE WHEN tblRe.StartDate = DATEADD(m, -1, GETDATE())
THEN t.total ELSE 0 END) AS prevMonth,
SUM(CASE WHEN tblRe.StartDate = DATEADD(m, -2, GETDATE())
THEN t.total ELSE 0 END) AS lastPrevMonth
FROM tblRe
INNER JOIN tblRv
ON tblRe.RID = tblRv.RID
INNER JOIN
(
SELECT MasterID, COUNT(*) AS total
FROM tblvv
GROUP BY MasterID
WHERE Name <> ''
) AS t
ON tblRv.ID = t.MasterID
WHERE tblRe.StartDate BETWEEN DATEADD(m, -2, GETDATE()) AND GETDATE() AND
tblRe.Region = 'uk' AND
tblRv.Owner = 'Roh'
GROUP BY tblRv.Owner
ORDER BY tblRv.Owner

Select rolling totals by month more data

I have a query that I have been using to track users sales. Previously they ere required to make a quota each month. Now, however they would like to change the rule to allow them to start any month, so they may go from June to June or whatever. they also want users to start over immediately if they miss a month. This does seem to be a more equitable system because if they didn't make the quota in March, for example, they were unable to count any they made after that month for the entire year. This really messes up my query though, and I don't know how to fix it. anyone have a solution?
here is the existing t-sql.
#Year int
AS
BEGIN
DECLARE #DateStart datetime
DECLARE #DateStop datetime
SELECT #DateStart = CONVERT(DATETIME, CONVERT( char(4), #Year) + '-01- 01')
SELECT #DateStop = CONVERT(DATETIME, CONVERT( char(4), #Year + 1) + '-01- 01')
SET NOCOUNT ON;
SELECT r.riderid,
r.dname,
DATEPART(yyyy, m.ridedate),
SUM(CASE DATEPART(mm, m.datesale) WHEN 1 THEN m.quota ELSE 0 END) AS [jan],
SUM(CASE DATEPART(mm, m.datesale) WHEN 2 THEN m.quota ELSE 0 END) AS [feb],
SUM(CASE DATEPART(mm, m.datesale) WHEN 3 THEN m.quota ELSE 0 END) AS [mar],
SUM(CASE DATEPART(mm, m.datesale) WHEN 4 THEN m.quota ELSE 0 END) AS [apr],
SUM(CASE DATEPART(mm, m.datesale) WHEN 5 THEN m.quota ELSE 0 END) AS [may],
SUM(CASE DATEPART(mm, m.datesale) WHEN 6 THEN m.quota ELSE 0 END) AS [jun],
SUM(CASE DATEPART(mm, m.datesale) WHEN 7 THEN m.quota ELSE 0 END) AS [jul],
SUM(CASE DATEPART(mm, m.datesale) WHEN 8 THEN m.quota ELSE 0 END) AS [aug],
SUM(CASE DATEPART(mm, m.datesale) WHEN 9 THEN m.quota ELSE 0 END) AS [sep],
SUM(CASE DATEPART(mm, m.datesale) WHEN 10 THEN m.quota ELSE 0 END) AS [oct],
SUM(CASE DATEPART(mm, m.datesale) WHEN 11 THEN m.quota ELSE 0 END) AS [nov],
SUM(CASE DATEPART(mm, m.datesale) WHEN 12 THEN m.quota ELSE 0 END) AS [dec],
SUM(m.quota) as [tot]
FROM users u
JOIN mysales m
ON m.riderid = u.riderid
Where m.datesale Between #DateStart AND #DateStop
GROUP BY DATEPART(yyyy, m.datesale), u.userid
ORDER BY DATEPART(yyyy, m.datesale), SUM(m.quota) DESC
END
OK -here is data
The table holds the users id, the customer id , amount of sale and date of sale
The query pulls the user, the sum of sales by month. User 250 made quota in July/2016, but did not in August, so he should get an entry in #quota for July, and September, but because he did not in August he has to restart in September; user# 300 has made quota from Jan 2016 to SEPT so he has qualified for his bonus as long as he finishes the 12 months. User 350 has successfully finished the year and should get a bonus. at this time in prod there is no quota table, would that simplify? What I need is a list of users that are in the running.
--drop table #sales
--drop table #quota
create table #sales
(
--saleid int --PK
userid int -- salesperson FK
, customerid int --FK
, sale_amt decimal
, date_sale datetime
)
insert into #sales values
(300,1301,542.90,'3-2-2016'),
(300,1301,782.70,'3-4-2016'),
(300,1541,600.70,'3-7-2016'),
(300,903,640.71,'3-10-2016'),
(300,1745,900.01,'3-29-2016'),
(300,1440,2040.71,'2-10-2016'),
(300,903,640.71,'2-20-2016'),
(300,414,1489.00,'1-18-2016'),
(300,1645,1322.00,'1-20-2016'),
(300,1200,1156.09,'4-2-2016'),
(300,1204,1456.00,'4-20-2016'),
(250,1140,156.89,'4-12-2016'),
(250,1240,1176.69,'4-14-2016'),
(250,840,480.61,'4-17-2016'),
(250,1940,500.71,'5-17-2016'),
(250,1425,4800.61,'6-1-2016'),
(250,1840,701.32,'6-15-2016'),
(250,1840,701.32,'7-15-2016'),
(250,1840, 2701.32,'8-15-2016'),
(450,8421,2500.61,'7-17-2015'),
(450,8422,2500.1,'8-17-2015'),
(450,843,2500.1,'9-17-2015'),
(450,8431,2500.00,'10-17-2015'),
(450,1431,2500.00,'11-17-2015'),
(450,4311,2500.00,'12-17-2015'),
(450,4310,2500.00,'1-17-2016'),
(450,1310,2500.00,'2-17-2016'),
(450,1310,2500.00,'3-17-2016'),
(450,130,2500.00,'4-17-2016'),
(450,1130,2500.00,'5-17-2016'),
(450,113,2500.00,'6-17-2016')
Select userid
, sum(sale_amt) Sale
, DATEPART(mm,date_sale) as[month]
from #sales
group by userid, DATEPART(mm,date_sale) order by userid
create table #quota
(
qid int --PK
, userid int -- salesperson FK
, quota bit -- awarded when sales => $2500.00
, datesale datetime -- date quota made
)
Just one possible way to write a query that looks back #running_months number of months to verify that no quotas have been missed during the window for each user:
select userid from users u
where not exists (
select 1 from #sales s
where s.userid = u.userid
and date_sale > dateadd(month, -#running_months - 1, current_timestamp)
and datediff(month, sales_date, current_timestamp) between 1 and #running_months
group by month(sales_amt)
having sum(sales_amt) < 2500
)
EDIT: I later realized that you probably do have users with no sales during a month so you'll probably need to actually verify the condition that all the months are over quota rather than none of the months are under quota:
select userid from users u
where userid in (
select userid from
(
select userid from #sales s
where s.userid = u.userid
and date_sale > dateadd(month, -#running_months - 1, current_timestamp)
and datediff(month, sales_date, current_timestamp) between 1 and #running_months
group by month(sales_amt)
having sum(sales_amt) >= 2500
) q
group by userid
having count(*) = #running_months
)

How to count the number of days in a given month and in a given year in MSSQL

EmployeeID RecordID DateRecord
1 1 2/19/2013 12:00:00 AM
1 2 2/21/2013 12:00:00 AM
1 3 2/23/2013 12:00:00 AM
1 4 2/27/2013 12:00:00 AM
1 5 3/3/2013 12:00:00 AM
2 11 3/10/2013 12:00:00 AM
2 12 3/14/2013 12:00:00 AM
1 14 3/16/2013 12:00:00 AM
How can I count the number of days?
Example in February 2013 which has "19, 21, 23, 27" that should be count to "4" days .. ??
I found this method ..
SELECT DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord),
COUNT(*)
FROM Records
GROUP BY DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord)
and resulted to ..
2013 2 19 1
2013 2 21 1
2013 2 23 1
2013 2 27 1
2013 3 3 1
2013 3 10 1
2013 3 14 1
2013 3 16 1
it just get the specific dates but didm't count the total number of days in each month .. help me .. pls
I have change few names hopr you won't mind
WITH Emp_CTE AS (
SELECT EmployeeID ,DATEPART(yy, Daterecord) AS years,
DATEPART(mm, Daterecord) AS months
-- DATEPART(dd, Daterecord) AS days
FROM testTrial
)
SELECT COUNT(months) AS noOfMonths ,* FROM Emp_CTE GROUP BY months,EmployeeID,years
SqlFiddle
Let you try this:-
1: Find the number of days in whatever month we're currently in
DECLARE #dt datetime
SET #dt = getdate()
SELECT #dt AS [DateTime],
DAY(DATEADD(mm, DATEDIFF(mm, -1, #dt), -1)) AS [Days in Month]Solution
2: Find the number of days in a given month-year combo
DECLARE #y int, #m int
SET #y = 2012
SET #m = 2
SELECT #y AS [Year],
#m AS [Month],
DATEDIFF(DAY,
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m - 1, 0)),
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m, 0))
) AS [Days in Month]
If your table is called Employee then this will do the trick:
select convert(varchar, DateRecord, 112)/ 100, count(*)
from Employee
group by convert(varchar, DateRecord, 112)/ 100
Your initial query was almost right, just needed to remove the DATEPART(dd, Daterecord) from the grouping and it would work. Add in a HAVING clause to find the records from the month of February:
SELECT
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
COUNT(1)
FROM
Records
GROUP BY
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord)
HAVING
DATEPART(yy, eCreationTime) = 2013
AND DATEPART(mm, Daterecord) = 2
there is no 'yearmonth' in the suggested code ??
try this perhaps
select
datename(month,daterecord) as [Month]
, year(DateRecord) as [Year]
, count(distinct DateRecord ) as day_count
, count(distinct dateadd(day, datediff(day,0, DateRecord ), 0)) as daytime_count
from your_table
where ( DateRecord >= '20130201' and DateRecord < '20130301' )
group by
datename(month,daterecord)
, year(DateRecord)
note the column [daytime_count] is only required if the field [DateRecord] has times othe than 12:00 AM (i.e. it "trims off" times so you deal with dates at 12:AM)
Regarding date range selections: many people will feel that using 'between' is the solution however that isn't true and the safest most reliable method is as I shown above. Note that the higher date is 1st March, but we are asking for information that is less than the 1st March, so we don't need to worry about leap years and we don't have to worry about hours and minutes either.
see: What do BETWEEN and the devil have in common?
try this...
declare #date2 nvarchar(max)
set #date2 = (select getdate())
select DateDiff(Day,#date2,DateAdd(month,1,#date2))

Select SQL results grouped by weeks

I want to select data from following table group by weeks
Date Product Name Sale
+----------+--------------+-----+
14-05-11 a 2
14-05-11 b 4
17-05-11 c 3
19-05-11 a 6
24-05-11 a 6
29-05-11 a 6
Let suppose today is 30-05-11
So my result should look like this.
Product Name First Week Second Week Third Week
+--------------------+------------+------------+-------------+
a 12 6 2
b 0 0 4
c 0 3 0
Will some body guide me to how to write SQL query to achieve this behavior!
I think this should do it..
Select
ProductName,
WeekNumber,
sum(sale)
from
(
SELECT
ProductName,
DATEDIFF(week, '2011-05-30', date) AS WeekNumber,
sale
FROM table
)
GROUP BY
ProductName,
WeekNumber
the provided solutions seem a little complex? this might help:
https://msdn.microsoft.com/en-us/library/ms174420.aspx
select
mystuff,
DATEPART ( year, MyDateColumn ) as yearnr,
DATEPART ( week, MyDateColumn ) as weeknr
from mytable
group by ...etc
This should do it for you:
Declare #DatePeriod datetime
Set #DatePeriod = '2011-05-30'
Select ProductName,
IsNull([1],0) as 'Week 1',
IsNull([2],0) as 'Week 2',
IsNull([3],0) as 'Week 3',
IsNull([4],0) as 'Week 4',
IsNull([5], 0) as 'Week 5'
From
(
Select ProductName,
DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, InputDate), 0), InputDate) +1 as [Weeks],
Sale as 'Sale'
From dbo.YourTable
-- Only get rows where the date is the same as the DatePeriod
-- i.e DatePeriod is 30th May 2011 then only the weeks of May will be calculated
Where DatePart(Month, InputDate)= DatePart(Month, #DatePeriod)
)p
Pivot (Sum(Sale) for Weeks in ([1],[2],[3],[4],[5])) as pv
It will calculate the week number relative to the month. So instead of week 20 for the year it will be week 2. The #DatePeriod variable is used to fetch only rows relative to the month (in this example only for the month of May)
Output using my sample data:
Base on #increddibelly answer, I applied to my query as below.
I share for whom concerned.
My table structure FamilyData(Id, nodeTime, totalEnergy)
select
sum(totalEnergy) as TotalEnergy,
DATEPART ( week, nodeTime ) as weeknr
from FamilyData
group by DATEPART (week, nodeTime)
Declare #DatePeriod datetime
Set #DatePeriod = '2011-05-30'
Select ProductName,
IsNull([1],0) as 'Week 1',
IsNull([2],0) as 'Week 2',
IsNull([3],0) as 'Week 3',
IsNull([4],0) as 'Week 4',
IsNull([5], 0) as 'Week 5'
From
(
Select ProductName,
DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, '2011-05-30'), 0), '2011-05-30') +1 as [Weeks],
Sale as 'Sale'
From dbo.WeekReport
-- Only get rows where the date is the same as the DatePeriod
-- i.e DatePeriod is 30th May 2011 then only the weeks of May will be calculated
Where DatePart(Month, '2011-05-30')= DatePart(Month, #DatePeriod)
)p
Pivot (Sum(Sale) for Weeks in ([1],[2],[3],[4],[5])) as pv
OUTPUT LOOK LIKE THIS
a 0 0 0 0 20
b 0 0 0 0 4
c 0 0 0 0 3

Resources