Select rolling totals by month more data - sql-server

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
)

Related

How can I merge two select queries with counts of mostly identical columns but one set returns multiple rows

I have two queries that work perfectly:
DECLARE #StartDate DATETIME = '2021-11-01 00:00:00';
DECLARE #EndDate DATETIME = '2022-03-16 23:59:59';
DECLARE #SalesEstimateTransactionTypeId INT = 16;
DECLARE #SalesOrderTransactionTypeId INT = 15;
SELECT
DATENAME(mm, GeneralJournal.[TransactionDate]) AS ReportingMonth,
DATEPART(mm, GeneralJournal.[TransactionDate]) AS MonthNumber,
DATEPART(yyyy, GeneralJournal.[TransactionDate]) AS ReportingYear,
COUNT(TransactionId) AS TransactionCount
FROM
GeneralJournal
WHERE
GeneralJournal.[TransactionDate] >= #StartDate
AND GeneralJournal.[TransactionDate] <= #EndDate
AND MasterRecord = 1
AND TransactionTypeId = #SalesEstimateTransactionTypeId
GROUP BY
DATEPART(yyyy, GeneralJournal.[TransactionDate]),
DATEPART(mm, GeneralJournal.[TransactionDate]),
DATENAME(mm,GeneralJournal.[TransactionDate]);
SELECT
DATENAME(mm, GeneralJournal.[TransactionDate]) AS ReportingMonth,
DATEPART(mm, GeneralJournal.[TransactionDate]) AS MonthNumber,
DATEPART(yyyy, GeneralJournal.[TransactionDate]) AS ReportingYear,
COUNT(DISTINCT TransactionId) AS ConversionCount
FROM
GeneralJournal
WHERE
GeneralJournal.[TransactionDate] >= #StartDate
AND GeneralJournal.[TransactionDate] <= #EndDate
AND MasterRecord = 0
AND TransactionTypeId = #SalesOrderTransactionTypeId
AND SEReferenceId > 0
GROUP BY
DATEPART(yyyy, GeneralJournal.[TransactionDate]),
DATEPART(mm, GeneralJournal.[TransactionDate]),
DATENAME(mm,GeneralJournal.[TransactionDate]);
Note that the second query returns distinct because it can return multiple values and we only want to count each TransactionId once in that scenario. These return the following results:
ReportingMonth
MonthNumber
ReportingYear
TransactionCount
November
11
2021
82
December
12
2021
49
January
1
2022
64
February
2
2022
67
March
3
2022
49
ReportingMonth
MonthNumber
ReportingYear
ConversionCount
November
11
2021
42
December
12
2021
27
January
1
2022
31
February
2
2022
50
March
3
2022
24
I actually need to combine them like this:
ReportingMonth
MonthNumber
ReportingYear
TransactionCount
ConversionCount
November
11
2021
82
42
December
12
2021
49
27
January
1
2022
64
31
February
2
2022
67
50
March
3
2022
49
24
I have tried pretty much everything I can think of - Unions, Joins, Subqueries - but so far nothing is quite right. This is the closest I can get:
SELECT
DATENAME(mm, GeneralJournal.[TransactionDate]) AS ReportingMonth,
DATEPART(mm, GeneralJournal.[TransactionDate]) AS MonthNumber,
DATEPART(yyyy, GeneralJournal.[TransactionDate]) AS ReportingYear,
SUM(CASE
WHEN TransactionTypeId = #SalesEstimateTransactionTypeId
AND MasterRecord = 1
THEN 1 ELSE 0
END) AS TransactionCount,
COUNT(CASE
WHEN TransactionTypeId = #SalesOrderTransactionTypeId
AND SEReferenceId > 0 THEN 1
END) AS ConversionCount
FROM
GeneralJournal
WHERE
GeneralJournal.[TransactionDate] >= #StartDate
AND GeneralJournal.[TransactionDate] <= #EndDate
AND TransactionTypeId IN (#SalesOrderTransactionTypeId, #SalesEstimateTransactionTypeId)
GROUP BY
DATEPART(yyyy, GeneralJournal.[TransactionDate]),
DATEPART(mm, GeneralJournal.[TransactionDate]),
DATENAME(mm,GeneralJournal.[TransactionDate]);
However, I am unable to find a way to get a Distinct value for the ConversionCount. As a result it is returning the full count:
ReportingMonth
MonthNumber
ReportingYear
TransactionCount
ConversionCount
November
11
2021
82
152
December
12
2021
49
67
January
1
2022
64
101
February
2
2022
67
136
March
3
2022
49
64
Can anyone guide me towards a way to combine the two query results whilst maintaining the Distinct on the conversion count? I must add that for it to work the answer must be compatible with both SQL Server and VistaDB the syntax of which is a subset of T-SQL because I am obliged to support both database engines with the same query.
EDIT - The Final Solution
Following on from Nick's excellent answer I was able embed the solution into my existing query code to ensure that there are results even for months with no records, shown here in case it helps anyone else:
DECLARE #StartDate DATETIME = '2021-11-01T00:00:00';
DECLARE #EndDate DATETIME = '2022-10-31T23:59:59';
DECLARE #SalesEstimateTransactionTypeId INT = 16;
DECLARE #SalesOrderTransactionTypeId INT = 15;
DECLARE #CurrentDate DATETIME;
DECLARE #Months TABLE(ReportingYear INT, MonthNumber INT, ReportingMonth VARCHAR (40));
-- Set the initial date
SET #CurrentDate = #StartDate
-- insert all dates into temp table
WHILE #CurrentDate <= #EndDate
BEGIN
INSERT INTO #Months VALUES(DATEPART(year, #CurrentDate), DATEPART(month, #CurrentDate), DATENAME(mm, #CurrentDate))
SET #CurrentDate = dateadd(mm, 1, #CurrentDate)
END;
SELECT ReportingMonth, ReportingYear, Coalesce(TransactionCount, 0) AS TransactionCount, Coalesce(ConversionCount,0) AS ConversionCount
FROM
(
SELECT months.[ReportingMonth], months.[ReportingYear], conversionData.[TransactionCount], conversionData.[ConversionCount]
FROM #Months months
LEFT JOIN
(
SELECT
ReportingMonth = DATENAME(mm, GeneralJournal.[TransactionDate]),
MonthNumber = DATEPART(mm, GeneralJournal.[TransactionDate]),
ReportingYear = DATEPART(yyyy, GeneralJournal.[TransactionDate]),
TransactionCount = SUM(CASE WHEN TransactionTypeId = #SalesEstimateTransactionTypeId AND GeneralJournal.[MasterRecord] = 1 THEN
1
ELSE
0
END
),
ConversionCount = COUNT(DISTINCT CASE WHEN GeneralJournal.[TransactionTypeId] = #SalesOrderTransactionTypeId
AND GeneralJournal.[SEReferenceId] > 0
AND GeneralJournal.[MasterRecord] = 0 THEN
GeneralJournal.[TransactionID]
END
)
FROM GeneralJournal
WHERE GeneralJournal.[TransactionDate] >= #StartDate
AND GeneralJournal.[TransactionDate] <= #EndDate
AND GeneralJournal.[TransactionTypeId] IN ( #SalesOrderTransactionTypeId, #SalesEstimateTransactionTypeId)
GROUP BY
DATEPART(yyyy, GeneralJournal.[TransactionDate]),
DATEPART(mm, GeneralJournal.[TransactionDate]),
DATENAME(mm, GeneralJournal.[TransactionDate])
) as conversionData
ON months.[ReportingYear] = conversionData.[ReportingYear] AND months.[MonthNumber] = conversionData.[MonthNumber]
) AS data;
You can just put the two columns in the same query. It is made more complicated by the fact that the WHERE clauses are slightly different. SO you need to group, then group again, and use conditional aggregation to count the right rows for each column.
Note the following:
You could in theory do COUNT(DISTINCT CASE however that is normally slower as the compiler will not recognize what the CASE is doing and instead do a full sort.
It is faster to group by a single EOMONTH calculation to group by a whole month. You can pull out the year and month in the SELECT.
COUNT(TransactionId) will return the number of non-null TransactionId values. if TransactionId cannot be null then COUNT(*) is the same thing.
If TransactionDate has a time component then you should use a half-open interval >= AND <
Use aliases on tables, it makes your queries more readable.
Use whitepsace, it's free.
DECLARE #StartDate DATETIME = '2021-11-01T00:00:00';
DECLARE #EndDate DATETIME = '2022-03-17T00:00:00';
DECLARE #SalesEstimateTransactionTypeId INT = 16;
DECLARE #SalesOrderTransactionTypeId INT = 15;
SELECT
DATENAME(month, gj.mth) AS ReportingMonth,
DATEPART(month, gj.mth) AS MonthNumber,
DATEPART(year , gj.mth) AS ReportingYear,
SUM(TransactionCount) AS TransactionCount,
COUNT(CASE WHEN ConversionCount > 0 THEN 1 END) AS ConversionCount
FROM (
SELECT
EOMONTH(gj.TransactionDate) AS mth,
gj.TransactionId,
COUNT(CASE WHEN gj.MasterRecord = 1 AND gj.TransactionTypeId = #SalesEstimateTransactionTypeId THEN 1 END) AS TransactionCount,
COUNT(CASE WHEN gj.MasterRecord = 0 AND gj.TransactionTypeId = #SalesOrderTransactionTypeId AND gj.SEReferenceId > 0 THEN 1 END) AS ConversionCount
FROM GeneralJournal gj
WHERE gj.TransactionDate >= #StartDate
AND gj.TransactionDate < #EndDate
AND gj.TransactionTypeId IN (#SalesOrderTransactionTypeId, #SalesEstimateTransactionTypeId)
GROUP BY
EOMONTH(gj.TransactionDate),
TransactionId
) g
GROUP BY
mth;
Your second query that is close, I think just has a couple of minor omissions.
You forgot MasterRecord = 0 in your ConversionCount CASE statement.
Instead of returning 1 or 0 from your ConversionCount CASE you should return TransactionID or NULL so you can still count distinct values.
You are missing DISTINCT inside of your ConversionCount COUNT.
You will need to handle NULL values in the ConversionCount COUNT. I assumed you will always have at one or more NULLs, so I just subtract 1 from the COUNT(DISTINCT ...) to compensate.
(I can't be 100% on the syntax here without some example detail data to work with.)
Code
SELECT
ReportingMonth = DATENAME(mm, GeneralJournal.TransactionDate),
MonthNumber = DATEPART(mm, GeneralJournal.TransactionDate),
ReportingYear = DATEPART(yyyy, GeneralJournal.TransactionDate),
TransactionCount = SUM(CASE
WHEN TransactionTypeId = #SalesEstimateTransactionTypeId
AND MasterRecord = 1 THEN
1
ELSE
0
END
),
ConversionCount = COUNT(DISTINCT CASE
WHEN TransactionTypeId = #SalesOrderTransactionTypeId
AND SEReferenceId > 0
AND MasterRecord = 0 THEN
TransactionID
ELSE
NULL
END
) - 1 /* Subtract 1 for the NULL */
FROM GeneralJournal
WHERE
GeneralJournal.TransactionDate >= #StartDate
AND GeneralJournal.TransactionDate <= #EndDate
AND TransactionTypeId IN (
#SalesOrderTransactionTypeId,
#SalesEstimateTransactionTypeId
)
GROUP BY
DATEPART(yyyy, GeneralJournal.TransactionDate),
DATEPART(mm, GeneralJournal.TransactionDate),
DATENAME(mm, GeneralJournal.TransactionDate);

How to add Totals in SQL

I am trying to get the totals of each month as of YTD (Years to date) Can someone please help me figure out how to integrate this in my query? thank you This is what I have so far.
DECLARE #Year int
set #Year = 2013
select a.first_name, a.last_name
, COUNT(CASE WHEN MONTH(b.Funded_date) = 1 THEN 1 ELSE NULL END) January
, COUNT(CASE WHEN MONTH(b.Funded_date) = 2 THEN 1 ELSE NULL END) February
, COUNT(CASE WHEN MONTH(b.Funded_date) = 3 THEN 1 ELSE NULL END) March
, COUNT(CASE WHEN MONTH(b.Funded_date) = 4 THEN 1 ELSE NULL END) April
From tContact a Join tContract b ON a.contact_id = b.contract_id
Group by a.first_name, a.last_name
This is just an example of how you could count up rows that fall under a certain month.
SELECT MONTH(b.Funded_date) AS 'MonthNum',
COUNT(*) AS 'Total'
FROM Table AS b
WHERE YEAR(b.Funded_date) = 2014
GROUP BY MONTH(b.Funded_date)
Hopefully this will help you with your query.
Thanks
What I tried to do here is create an upper bound record for each month in tContract then join that back into the query you already had. It is joined on dates that are between the beginning of the year and the current month.
DECLARE #Year int
set #Year = 2013
select Ms.thismonth, count(B.thing_You_are_Totalling) from (
select thisMonth = dateadd(month,datediff(month,0,Funded_date),0)
from tContract
where moid = 2005405
and year(Funded_date) = #Year
group by dateadd(month,datediff(month,0,Funded_date),0)
) Ms
inner join (select * from tContact a inner join tContract ON a.contact_id = tContract.contract_id) B
on B.Funded_date >=dateadd(year,datediff(year,0,B.Funded_date),0) -- beginning of year
and B.Funded_date <= Ms.thisMonth -- this month
where year(B.Funded_date) = #Year -- restrict to only this year
group by thisMonth, first_name, last_name
I don't have your full table definition so there might be some issues (maybe a SqlFiddle is in order)
Not sure if this is what you are asking for.
select a.first_name, a.last_name
, COUNT(CASE WHEN MONTH(b.Funded_date) = 1 THEN 1 ELSE NULL END) January
, COUNT(CASE WHEN MONTH(b.Funded_date) = 2 THEN 1 ELSE NULL END) February
, COUNT(CASE WHEN MONTH(b.Funded_date) = 3 THEN 1 ELSE NULL END) March
, COUNT(CASE WHEN MONTH(b.Funded_date) = 4 THEN 1 ELSE NULL END) April
, COUNT(*) TotalCount
, SUM(CASE WHEN MONTH(b.Funded_date) = 1 THEN Amount ELSE NULL END) JanuaryAmount
, SUM(CASE WHEN MONTH(b.Funded_date) = 2 THEN Amount ELSE NULL END) FebruaryAmount
, SUM(CASE WHEN MONTH(b.Funded_date) = 3 THEN Amount ELSE NULL END) MarchAmount
, SUM(CASE WHEN MONTH(b.Funded_date) = 4 THEN Amount ELSE NULL END) AprilAmount
From tContact a Join tContract b ON a.contact_id = b.contact_id
WHERE YEAR(b.Funded_date) = #Year
Group by a.first_name, a.last_name
How about this:
declare #year int = 2013
select a.first_name
, a.last_name
, month(b.Funded_date) [Month]
, datename(month, dateadd(month, month(date_of_birth_dt), - 1)) [MonthName]
, count(month(b.Funded_date)) [Total]
from tContact a
where a.[Year] = #year
group by a.first_name, a.last_name, month(b.Funded_date)
It returns the total of each Month for the year 2013. a.[Year] might not the the name of the field that you have so adjust accordingly. Also, [Month] returns numeric value for month.
Use the Count(*) As Total function. I'm sure this will help you
SELECT MONTH(b.Funded_date) AS 'MonthNum',
COUNT(*) AS 'Total'
FROM Table AS b
WHERE YEAR(b.Funded_date) = 2014
GROUP BY MONTH(b.Funded_date)

How to display month name in different columns in sql using datediff function

I have this query:
DECLARE #month INT
SET #month=1
SELECT
CLOI_ClientOrderItems.cl_Id,
NoOfInv = SUM(CASE WHEN DATEPART(mm, in_date_issued) <= #month
AND DATEPART(yyyy, in_date_issued) = 2014
THEN 1 ELSE 0 END),
MonthTotal = SUM(CASE WHEN DATEPART(mm, in_date_issued) <= #month
AND DATEPART(yyyy, in_date_issued) = 2014
THEN in_total ELSE 0 END),
Grandtotal = SUM(in_total),
RemainingAmount = SUM(in_total) - SUM(CASE
WHEN DATEPART(mm, in_date_issued) <= #month
THEN in_total ELSE 0 END)
FROM (SELECT
DISTINCT MasterOrderId, cl_Id
FROM
CLOI_ClientOrderItems) as CLOI_ClientOrderItems
INNER JOIN
IN_Invoices
ON
IN_Invoices.MasterOrderId = CLOI_ClientOrderItems.MasterOrderId
GROUP BY
CLOI_ClientOrderItems.cl_id
i want output like
noofinv |amt |clid | grandtotal | jan |feb |march |remainingamt
5 |7.00 |100000_Pri | 245.00 | 0.00 |238.00 |7.00 |238.00
12 |2510.12 |100001_pro | 181110.29 | 138891.92 |9708.25 |510.12 |178600.17
If I pass month number like 3, it should display it as like Jan Feb and March and its related records in the respective month.
Try this, where condition is useful for you.
select Datename(mm,in_date_issued) from IN_Invoices Where Datediff(yyyy,in_date_issued,3) = 2014 and Datediff(mm,in_date_issued,#monthParameter) <= 3
You can Try like This...
declare #MonthCount int=1
SELECT DATENAME(month, DATEADD(month, #MonthCount-1 ,
CAST('2014-01-01' AS datetime))) as Month_Name
OP:
Month_Name
January

Quickest way to get aggregate date statistics / pivot

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

T-SQL Pivot to display One Comment field per week

I've been asked to change a pivot query that currently displays a week ending date, category and hours by date for one week in a row. I've been asked to display a comment field at the end of the row and I can't figure out how to alter the query to do this.
The table is structured like this
Category Date Comments Hours
test 8/2/2010 myComment 2
test 8/3/2010 8
test 8/4/2010 4
test 8/5/2010 3
test 8/6/2010 5
I would like the data to display like this. I have a query that will diplay all of this except the comment. On the front End I'm only going to allow one comment per week and add it to the Monday date row in the table for each week/category combination.
WeekEnding Category SunHrs MonHrs TuesHrs WedHrs ThuHrs FriHrs SatHrs Comment
8/7/2010 test 0 1 1 1 1 1 1 myComment
Here is the query before adding the comment field which works fine.
DECLARE #WeekEnding datetime
DECLARE #UserName nvarchar(245)
SET #WeekEnding = '09/04/2010'
SET #UserName = 'brogers'
SELECT
#WeekEnding WeekEnding
,CategoryID
,isnull([1], 0) SunHrs
,isnull([2], 0) MonHrs
,isnull([3], 0) TueHrs
,isnull([4], 0) WedHrs
,isnull([5], 0) ThuHrs
,isnull([6], 0) FriHrs
,isnull([7], 0) SatHrs
from (select CategoryID, Datepart(dw, TimeEntryDate) DOW, TimeEntryDuration Hours
from dbo.aspnet_starterkits_TimeEntry
where TimeEntryDate between dateadd(dd, -6, #WeekEnding) and #WeekEnding) Source
pivot (max(Hours) for DOW in ([1],[2],[3],[4],[5],[6],[7]) ) as pvt
I'm not sure how to add the comment field to the end of the row. When I do add it I get a result like this
WeekEnding Category SunHrs MonHrs TuesHrs WedHrs ThuHrs FriHrs SatHrs Comment
8/7/2010 test 0 0 1 1 1 1 0
8/7/2010 test 0 1 0 0 0 0 0 myComment
I only want one row per weekending/category combination and one comment per row in the output.
Here is the query that where I added the comment field and displays incorrectly.
Can anyone point out how to display one comment per week/categroy row?
DECLARE #WeekEnding datetime
DECLARE #UserName nvarchar(245)
SET #WeekEnding = '09/04/2010'
SET #UserName = 'brogers'
SELECT
#WeekEnding WeekEnding
,TimeEntryDescription
,CategoryID
,isnull([1], 0) SunHrs
,isnull([2], 0) MonHrs
,isnull([3], 0) TueHrs
,isnull([4], 0) WedHrs
,isnull([5], 0) ThuHrs
,isnull([6], 0) FriHrs
,isnull([7], 0) SatHrs
from (select
CategoryID,
Datepart(dw, TimeEntryDate) DOW,
TimeEntryDuration Hours,
TimeEntryDescription
from dbo.aspnet_starterkits_TimeEntry
where TimeEntryDate between dateadd(dd, -6, #WeekEnding) and #WeekEnding) Source
pivot (max(Hours) for DOW in ([1],[2],[3],[4],[5],[6],[7]) ) as pvt
Even though the source table has a comment field for every day of the week, I only want one comment per week and one row per week/category combination.
I will restrict the input to only allow one per week (monday for example) and want this one comment to display at the end of the row on the output query.
This is probably easier with an old style pivot
;With [Source] As
(
select
CategoryID,
Datepart(dw, TimeEntryDate) DOW,
TimeEntryDuration Hours,
TimeEntryDescription
from dbo.aspnet_starterkits_TimeEntry
where TimeEntryDate between dateadd(dd, -6, #WeekEnding) and #WeekEnding
)
SELECT
#WeekEnding WeekEnding
,TimeEntryDescription
,CategoryID
,max(case when DOW = 1 then [Hours] else 0 end) SunHrs
,max(case when DOW = 2 then [Hours] else 0 end) MonHrs
,max(case when DOW = 3 then [Hours] else 0 end) TueHrs
,max(case when DOW = 4 then [Hours] else 0 end) WedHrs
,max(case when DOW = 5 then [Hours] else 0 end) ThuHrs
,max(case when DOW = 6 then [Hours] else 0 end) FriHrs
,max(case when DOW = 7 then [Hours] else 0 end) SatHrs
,max(comment) as comment
from [Source]
group by
TimeEntryDescription
,CategoryID
Join the pivot table with the source table on date and retrieve min(comment).
DECLARE #WeekEnding datetime
DECLARE #UserName nvarchar(245)
SET #WeekEnding = '09/04/2010'
SET #UserName = 'brogers'
;with Report as (
SELECT
#WeekEnding WeekEnding
,TimeEntryDescription
,CategoryID
,isnull([1], 0) SunHrs
,isnull([2], 0) MonHrs
,isnull([3], 0) TueHrs
,isnull([4], 0) WedHrs
,isnull([5], 0) ThuHrs
,isnull([6], 0) FriHrs
,isnull([7], 0) SatHrs
from (select
CategoryID,
Datepart(dw, TimeEntryDate) DOW,
TimeEntryDuration Hours,
TimeEntryDescription
from dbo.aspnet_starterkits_TimeEntry
where TimeEntryDate between dateadd(dd, -6, #WeekEnding) and #WeekEnding) Source
pivot (max(Hours) for DOW in ([1],[2],[3],[4],[5],[6],[7]) ) as pvt
)
select *
,(select min(comment) from dbo.aspnet_starterkits_TimeEntry te where TimeEntryDate between dateadd(dd, -6, #WeekEnding) and #WeekEnding)) [Comment]
from Report r

Resources