How to calculate a daily running total from an aliased column - sql-server

I'm writing a query to return a table of booking data for an event. I've used a SUM aggregate to group the number of daily bookings. I'd now like to create a column with a cumulative running total.
I can't make my query work because (I'm presuming) it doesn't like the aliased column title being included in the aggregate function.
Please can anyone advise me on the best approach to make this work?
SELECT
'Recruitment Event' Event,
CAST(ep.CREATIONDATE AS date) AS 'Date of Booking',
SUM(CASE
WHEN ep.CREATIONDATE IS NOT NULL THEN 1
ELSE 0
END) 'Total Bookings',
(SELECT
SUM('Total Bookings')
FROM EVENTPLACE AS ep
WHERE CAST(ep.CREATIONDATE AS date) <= CAST(ep.CREATIONDATE AS date))
'Running Total'
FROM EVENTPLACE AS ep
LEFT JOIN EVENTMODULE AS em
ON em.EVENTMODULENO = ep.EVENTMODULENO
WHERE em.EVENTMODULENO = '11111111-ABCD-1234-1234-1010101010'
GROUP BY CAST(ep.CREATIONDATE AS date)
ORDER BY CAST(ep.CREATIONDATE AS date) DESC

try this
SELECT
[Event] = 'Recruitment Event' ,
[Date of Booking] = CAST(ep.CREATIONDATE AS DATE),
[Total Bookings] = COUNT(ep.CREATIONDATE),
[Running Total] = SUM(COUNT(ep.CREATIONDATE)) OVER (ORDER BY CAST(ep.CREATIONDATE AS DATE) DESC)
FROM
EVENTPLACE AS ep
WHERE
EXISTS (SELECT
*
FROM
EVENTMODULE em
WHERE
em.EVENTMODULENO = ep.EVENTMODULENO
AND em.EVENTMODULENO = '11111111-ABCD-1234-1234-1010101010')
GROUP BY
CAST(ep.CREATIONDATE AS DATE)
ORDER BY
CAST(ep.CREATIONDATE AS DATE) DESC
COUNT(ep.CREATIONDATE) will ignore null values with the same result as your SUM(CASE)
You dont really have to use EXISTS, but you should probably change your LEFT JOIN to a JOIN since you're using the em.EVENTMODULENO in the WHERE statement
SUM(COUNT(ep.CREATIONDATE)) OVER () will give you running total of the count. You use ORDER BY in the OVER to determine the order of the SUM. Since you're ordering by CAST(ep.CREATIONDATE AS DATE) DESC you can just use that in the OVER()

You are getting error because of these lines..
SUM (case when ep.CREATIONDATE IS NOT NULL then 1 else 0 end) 'Total Bookings',
(SELECT SUM ('Total Bookings') FROM EVENTPLACE
You are trying to select alias which was defined in same phase and you are passing it as string .your query wont work unless you correlate it with main table which you are not doing..
Below query works from SQLServer 2012
;with cte
as
(
SELECT 'Recruitment Event' Event,
cast(ep.CREATIONDATE as date) as 'Date of Booking',
SUM (case when ep.CREATIONDATE IS NOT NULL then 1 else 0 end) 'Total Bookings',
FROM EVENTPLACE as ep
LEFT JOIN EVENTMODULE as em ON em.EVENTMODULENO=ep.EVENTMODULENO
WHERE em.EVENTMODULENO = '11111111-ABCD-1234-1234-1010101010'
GROUP BY cast(ep.CREATIONDATE as date)
)
select *,
sum([Total Bookings])
over (order by [Date of Booking] ROWS UNBOUNDED ECEDING ) as 'Runningtotal'
from
cte

The problem is that there is a SUM() function in your select statement, but not all the other columns are included in your group by clause.
The solution is that add an OVER(ORDER BY ...) clause after your SUM function to calculate the running total.
SELECT
'Recruitment Event' Event,
CAST(ep.CREATIONDATE AS date) AS 'Date of Booking',
SUM(CASE
WHEN ep.CREATIONDATE IS NOT NULL THEN 1
ELSE 0
END)OVER(ORDER BY CAST(ep.CREATIONDATE AS date) DESC) 'Running Total',
FROM EVENTPLACE AS ep
LEFT JOIN EVENTMODULE AS em
ON em.EVENTMODULENO = ep.EVENTMODULENO
WHERE em.EVENTMODULENO = '11111111-ABCD-1234-1234-1010101010'
CAST(ep.CREATIONDATE AS date) DESC

Related

Average day gap in between a repeat order for each product

Can someone please help me to find the average time between first and second purchase on a product level.
This is what I have written -
Select A.CustomerId,A.ProductId , A.OrderSequence, (Case WHEN OrderSequence = 1 THEN OrderDate END) AS First_Order_Date,
MAX(Case WHEN OrderSequence = 2 THEN OrderDate END) AS Second_Order_Date
From
(
Select t.CustomerId, t.ProductId, t.OrderDate,
Dense_RANK() OVER (PARTITION BY t.CustomerId, t.ProductId ORDER BY OrderDate Asc) as OrderSequence
From Transactions t (NOLOCK)
Where t.SiteKey = 01
Group by t.CustomerId, t.ProductId, t.OrderDate)
A
Where A.OrderSequence IN (1,2)
Group By A.Customer_Id, A.ProductId, A.OrderSequence, A.OrderDate
Sample Data:
It looks like row-numbering and LEAD should do the trick for you here.
Don't use NOLOCK unless you really know what you're doing
It's unclear if you want the results to be partitioned by CustomerId also. If not, you can remove it everywhere in the query
SELECT
A.CustomerId,
A.ProductId,
AVG(DATEDIFF(day, OrderDate, NextOrderDate))
FROM
(
SELECT
t.CustomerId,
t.ProductId,
t.OrderDate,
ROW_NUMBER() OVER (PARTITION BY t.CustomerId, t.ProductId ORDER BY OrderDate) AS rn,
LEAD(OrderDate) OVER (PARTITION BY t.CustomerId, t.ProductId ORDER BY OrderDate) AS NextOrderDate
FROM Transactions t
WHERE t.SiteKey = '01'
) t
WHERE t.rn = 1
GROUP BY
t.Customer_Id,
t.ProductId;

TSQL group by generate duplicate row

I'm trying to extract all prices and taxes by dates range (not necessary the same date) in 2 column and group by ID.
Because I need to group by 2 others columns because T-SQL need that:
Column '...' is invalid in the select list because
it is not contained in either an aggregate function or the GROUP BY clause.
I have a duplicate user/ID sometimes. ( don't know why by the way..)
I have this SQL:
WITH myQuery AS
(
Select
c.name, c.id,
CASE
WHEN g.dateCreated BETWEEN CAST ('2016-06-01 00:00:00.000' AS DATETIME)
AND CAST ('2017-05-31 23:59:59.000' AS DATETIME)
THEN SUM(CAST(g.price AS decimal(20,2) ))
ELSE 0
END AS TOTAL_PRICE,
CASE
WHEN g.dateCreated BETWEEN CAST ('2016-01-01 00:00:00.000' AS DATETIME)
AND CAST ('2016-12-31 23:59:59.000' AS DATETIME)
THEN SUM(CAST(g.tax AS decimal(20,2) ))
ELSE 0
END AS TOTAL_TAX
FROM customers c
inner join goodies g
ON c.id = g.customer_id
GROUP BY c.name, c.id, g.dateCreated
)
SELECT count(*) FROM myQuery
I got 5203 rows. I have only 5031 users.
When I Analyse my data, I have some duplicate data.
Example:
Alex, 12, 0.00, 0.00
Alex, 12, 100.00, 14.55
Nancy, 4, 0.00, 0.00
Arthur, 97, 48.14, 09.17
I tried to group by only by id but it seem that I can't do that.
Why I have a duplicate data and How to prevent that and ensure that I have 1 row by USER even if they don't buy goodies?
Correcting your conditional aggregation and removing dateCreated from the group by:
with myQuery as (
select
c.name
, c.id
, total_price = sum(case
when g.dateCreated >= '20160601' and g.dateCreated < '20170601'
then cast(g.price as decimal(20,2))
else 0
end)
, total_tax = sum(case
when g.dateCreated >= '20160101' and g.dateCreated < '20170101'
then cast(g.tax as decimal(20,2))
else 0
end)
from customers c
left join goodies g
on c.id = g.customer_id
group by
c.name
, c.id
--, g.dateCreated
)
select count(*) from myQuery;
Changing the inner join to a left join will return customers even if they have no corresponding row in goodies.
I also changed your date range code to be more explicit about what is included.
Reference:
Bad habits to kick : mis-handling date / range queries - Aaron Bertrand
What do between and the devil have in common? - Aaron Bertrand

How to get a list of months and year between two dates in SQL Server

I have to get the list of months and year in between my dates. Currently it only returns month and year for dates that has data associated with it.
for example my dates is between: '8'+'/1'+'/'+'2015' and DATEADD(mm, 15, '8'+'/1'+'/'+'2016'
It only prints out: May2016, June2016, July2016, Auguest2016, September2016
I want it to print out all of the months and year in between. Here is my sql queries:
select d.id_base as case_id,
c.C_LAST_ACTION AS Docketed,
c.C_CASE_TYPE AS caseType,
ct.C_NAME As caseName,
ct.C_DESCRIPTION AS caseNameDescription,
case when d.c_mod_decision_id is not null then '' else DATENAME(mm, d.c_issue_date) + DATENAME(yyyy, d.c_issue_date) end as display
from t_case_decision d JOIN T_CASE_INPUT c on c.id = d.id_base JOIN T_CASE_TYPE ct on C_CASE_TYPE = ct.id
where cast(d.c_issue_date AS date) BETWEEN '8'+'/1'+'/'+'2015' and DATEADD(mm, 15, '8'+'/1'+'/'+'2016')
First, create a numbers table
CREATE TABLE Numbers(N INT)
insert into Numbers(N)
select top 1000000 row_number() over(order by t1.number) as N
from master..spt_values t1
cross join master..spt_values t2
then use DATEADD to list dates between desired values, like this
declare #iniDate as date
set #iniDate='20150801'
select dateadd(MONTH,N,#iniDate) dates
from Numbers
where N<15 order by N
These returns dates from #iniDate up to 15 months later
EDIT: try this, I don't have sql right now
select datename(mm, dateadd(MONTH,N,#iniDate))+datename(yyyy ,dateadd(MONTH,N,#iniDate)) display
from ( select top 15row_number() over(order by t1.number) as N
from master..spt_values t1
cross join master..spt_values t2) numbers right join (
select d.id_base as case_id,
c.C_LAST_ACTION AS Docketed,
c.C_CASE_TYPE AS caseType,
ct.C_NAME As caseName,
ct.C_DESCRIPTION AS caseNameDescription,
case when d.c_mod_decision_id is not null then '' else DATENAME(mm, d.c_issue_date) + DATENAME(yyyy, d.c_issue_date) end as display
from t_case_decision d JOIN T_CASE_INPUT c on c.id = d.id_base JOIN T_CASE_TYPE ct on C_CASE_TYPE = ct.id
where cast(d.c_issue_date AS date) BETWEEN '8'+'/1'+'/'+'2015' and DATEADD(mm, 15, '8'+'/1'+'/'+'2016')
sql-server
) qq
on datename(mm, dateadd(MONTH,N,#iniDate))+datename(yyyy ,dateadd(MONTH,N,#iniDate)) = qq.display
where N<15 order by N
If I understand what you're trying to accomplish, a recursive CTE might help. Here's a quick example of what you can do. The CTE will expand out into a list of dates, which you can then use as the base for your query.
The contents of the TargetData CTE may need to be adjusted, as I don't have a complete picture of your data structure.
DECLARE #startDate DATE = '1/1/2015';
DECLARE #endDate DATE = '7/31/2016';
-- Recursive CTE to generate a list of months within the date range:
WITH Months AS (
SELECT CONVERT(DATE, DATEADD(D, -(DAY(#startDate)) + 1, #startDate)) [MonthDate]
UNION ALL
SELECT DATEADD(M, 1, MonthDate)
FROM Months
WHERE MonthDate <= DATEADD(M, -1, #endDate)
),
TargetData AS (
-- This is a slightly modified version of the original query:
select
d.id_base as case_id,
c.C_LAST_ACTION AS Docketed,
c.C_CASE_TYPE AS caseType,
ct.C_NAME As caseName,
ct.C_DESCRIPTION AS caseNameDescription,
case when d.c_mod_decision_id is not null then '' else DATENAME(mm, d.c_issue_date) + DATENAME(yyyy, d.c_issue_date) end as display,
-- Return the "MonthDate" so that it can be left joined to the Months table:
DATEADD(D, -(DAY(d.c_issue_date)) + 1, d.c_issue_date) [MonthDate]
from t_case_decision d JOIN T_CASE_INPUT c on c.id = d.id_base JOIN T_CASE_TYPE ct on C_CASE_TYPE = ct.id
where cast(d.c_issue_date AS date) BETWEEN #startDate AND #endDate
)
SELECT
m.MonthDate,
DATENAME(mm, m.MonthDate) + DATENAME(yyyy, m.MonthDate),
td.*
FROM Months m
LEFT JOIN TargetData td ON td.MonthDate = m.MonthDate;
You need to join on primary keys between tables, I haven't seen a between statement with that syntax. So I suggest trying the following:
SELECT d.id_base as case_id, c.C_LAST_ACTION AS 'Docketed',c.C_CASE_TYPE AScaseType,ct.C_NAME As 'caseName', ct.C_DESCRIPTION AS 'caseNameDescription'
,CASE
WHEN d.c_mod_decision_id is not null THEN '' AS 'null_val'
ELSE CONCAT(YEAR(d.c_issue_dateDATENAME), MONTH(d.c_issue_date))
END AS 'display'
FROM t_case_decision d INNER JOIN T_CASE_INPUT c on c.id = d.id_base
INNER JOIN T_CASE_TYPE ct on c.id = ct.id
WHERE CONVERT(DATE,d.c_issue_date) BETWEEN '08/01/2015'
AND '08/01/2016';
I hope this helps or points you in the right direction :)

mssql distinct count in subquery

I have sql something like
select
name,col2,col3,date
from
table1 join on few tables
And result is
name col2 col3 date
a a a datetime1
a a a datetime1
b b b datetime2
b b b datetime3
and i dont know how to do, but i need to replace date column with column which will
shows count of working days for every name/row
I though about subquery like
,(select COUNT(distinct DATENAME(dw, date) NOT IN ('Saturday', 'Sunday')) where name = '...' from ... where ...) as WorkingDays
but i need help to get it working, thank you.
Perhaps you want this:
SELECT Name, C2,
WorkingDays = Sum(CASE WHEN Datename(DW, [date])IN( 'Saturday', 'Sunday' )
THEN 0 ELSE 1 END)
OVER (PARTITION BY Name)
FROM dbo.data
Here is the sql-fiddle with your sample-data: http://sqlfiddle.com/#!3/1279f/14/0
if my understanding is not wrong you just need a groupby of other columns and count of 'date'
select
name,col2,col3,count(date) as no_of_days
from
table1 join on few tables
where DATENAME(dw, date) NOT IN ('Saturday', 'Sunday')
group by name,col2,col3

how to query two column of same table with two condition with groupby

Table :tbl_user
dateofregistration ID registrationstate
6-03-11 3 0
6-03-11 1 0
6-03-11 2 1
7-03-11 2 1
7-03-11 1 1
how can I display result like this for sql server 2008 express
date TotalID(count) Total State(0 only)
6-03-11 3 2
7-03-11 2 0
I have tried with this
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
(select COUNT(ID)) AS Subbase,
(Select Count(ID)from tbl_User where (registrationstate='0')) AS Totalchurn
FROM tbl_User
GROUP BY CONVERT(varchar(10), dateofregistration, 103);
but wrong result.Any help plz.
How about;
select
cast(dateofregistration as date),
count(distinct id), --or * for all
sum(
case registrationstate when '0' then 1 else 0 end
)
from tbl_user
group by cast(dateofregistration as date)
order by 1
2011-06-03 3 2
2011-07-03 2 0
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
COUNT(1) AS Subbase,
SUM(CASE WHEN registrationstate='0' THEN 1 ELSE 0 END) AS Totalchurn
FROM tbl_User
GROUP BY CONVERT(varchar(10), dateofregistration, 103)
ORDER BY 1
You were nearly there. You don't need a subselect for COUNT(ID) since that is handled by the GROUP BY. You group by date, and so the count will be the number of IDs within each date. I've made the count distinct, just in case you can have multiple registrations of the same ID on one day.
Your subquery was almost right - it needs to be correlated with the main query by selecting rows with the same registration date.
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
COUNT(DISTINCT ID) AS TotalID,
(Select Count(*) from tbl_User t2 where (registrationstate='0') AND t2.registrationdate=t1.registrationdate) AS Totalchurn
FROM tbl_User t1
GROUP BY CONVERT(varchar(10), dateofregistration, 103);

Resources