I have to query a set of running total data by month.
e.g.
Month Amount Total
2014-01-01 100 100
2014-01-02 100 200
2014-01-03 100 300
The application does not allow to create a view or SP. It is able to select data from a table directly.
e.g.
select Month,
Amount,
Total -- This is my problem.
from Table -- This is a table only.
Any ideas are welcome, thank you.
You can use OUTER APPLY:
SELECT T.Month,T.Amount,T2.Total
FROM Table1 T
OUTER APPLY
( SELECT Total = SUM(Amount)
FROM Table1 T2
WHERE T2.Month <= T.Month
) T2;
Or a correlated subquery:
SELECT T.Amount,
( SELECT Amount = SUM(Amount)
FROM Table1 T2
WHERE T2.Month <= T.Month
)
FROM Table1 T
The easiest way is to use SQL Server 2012 because it has cumulative sum built-in:
select Month, Amount,
sum(Amount) over (order by Month) as Total -- This is my problem.
from Table;
The correlated subquery method follows a similar structure:
select Month, Amount,
(select sum(Amount) from table t2 where t2.Month <= t.Month) as Total
from Table t;
These are usually the two methods that I would consider, because both are standard SQL. As Vignesh points out you can do it with cross apply as well (although as I write this, his query is not correct).
Here is a second way to create a running total:
SELECT t.month, t.amount,
SUM(t.amount) OVER(PARTITION BY t.month ORDER BY t.month
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as [Total]
FROM [yourTable] AS t
Related
I have two table with columns as mentioned below:
CUSTOMER table as DIM_CUSTOMER:
ID_CUSTOMER, CUSTOMER_NAME
TRANSACTION table as FACT_TRANSACTION:
ID_CUSTOMER, DATE, TOTAL_PRICE, QUANTITY
Problem statement is to
Find top 100 customers and their average spend, average quantity by each year. Also find the percentage of change in their spend.
My approach:
SELECT TOP 100
YEAR(FT.DATE) AS [YEAR],
FT.ID_CUSTOMER AS [CUSTOMER NAME],
FT.TOTAL_PRICE AS [TOTAL AMT],
AVG(FT.TOTAL_PRICE) AS [AVG SPEND],
AVG(FT.QUANTITY) AS [AVG QUANTITY]
FROM
FACT_TRANSACTIONS FT
INNER JOIN
DIM_CUSTOMER DC ON FT.ID_CUSTOMER = DC.ID_CUSTOMER
GROUP BY
FT.DATE, FT.ID_CUSTOMER, FT.TOTAL_PRICE
ORDER BY
3 DESC
This is resulting in the top 100 customers based on their usage.
Now I need to determine the percentage change in their spend YEAR wise.
How can I do that? Probably using PIVOT option herein will help, but I'm unsure.
You can try using LAG in order to access the previous [AVG SPEND] for the current row. The idea is to group the data for each [CUSTOMER NAME] using PARTITION BY and then to order the data by the [YEAR]. The function will give us the previous result and we can calculated easily the difference.
Try something like this:
SELECT TOP 100
YEAR(FT.DATE) AS [YEAR],
FT.ID_CUSTOMER AS [CUSTOMER NAME],
FT.TOTAL_PRICE AS [TOTAL AMT],
AVG(FT.TOTAL_PRICE) AS [AVG SPEND],
AVG(FT.QUANTITY) AS [AVG QUANTITY]
INTO #DataSource
FROM
FACT_TRANSACTIONS FT
INNER JOIN
DIM_CUSTOMER DC ON FT.ID_CUSTOMER = DC.ID_CUSTOMER
GROUP BY
YEAR(FT.DATE), FT.ID_CUSTOMER, FT.TOTAL_PRICE
ORDER BY
[AVG SPEND] DESC
SELECT *
,[AVG SPEND] - LAG([AVG SPEND], 1, 0) OVER (PARTITION BY [CUSTOMER NAME] ORDER BY [YEAR])
FROM #DataSource
Note, that:
the function requires SQL Server 2012+
you can change the partitioning and ordering as you like in order to satisfy your real goal (for example you can use ORDER BY [YEAR] DESC
you can use the LEAD function in order to access the next value within the group if you want to calculated difference in advace
I materialized the data in temporary table, but you can use table variable or whatever you are using
Can you please try following SQL CTE query ?
;with topcustomers as(
SELECT distinct top 100
ID_CUSTOMER,
SUM(TOTAL_PRICE) over (partition by ID_CUSTOMER) as TotalSPEND
FROM FACT_TRANSACTION
order by TotalSPEND desc
), cte as (
SELECT
distinct
t.ID_CUSTOMER, YEAR(t.DATE) [YEAR], TotalSPEND,
AVG(t.QUANTITY * 1.0) over (partition by t.ID_CUSTOMER, YEAR(t.DATE)) as AverageQUANTITY,
AVG(t.TOTAL_PRICE * 1.0) over (partition by t.ID_CUSTOMER, YEAR(t.DATE)) as AverageSPEND
FROM FACT_TRANSACTION t
INNER JOIN topcustomers c on c.ID_CUSTOMER = t.ID_CUSTOMER
)
select
*,
( AverageSPEND - lag(AverageSPEND,1) over (partition by ID_CUSTOMER order by [YEAR]) ) * 100.0 / AverageSPEND as [%Change]
from cte
I have a table and it looks like
Table I have
I need to create a table that consists of the following columns
Table I need
Table includes unique ID, min date when the number of sms was max and max date when the number of sms was min, including the numbers of sms for both cases
I've tried several queries, but I feel that the problem is not in finding min(sms) in a column "sms" in general but with a condition max(date) for the column "date"
So if I have to find maximum amount of sms sent on minimum data, I should pick the minimum data when the number was maximum. Now I can only find the number of sms sent on min data.
Will be glad for any explanation
P.S.: I'm using MS SQL 2014
That should work or at least starting point to tune:
SELECT * INTO Table1 FROM (VALUES
(1,'1/1/2015',10),
(1,'2/1/2015',10),
(1,'3/1/2015',20),
(1,'4/1/2015',20),
(2,'5/1/2015',30),
(2,'6/1/2015',30),
(2,'7/1/2015',40),
(2,'8/1/2015',40)
) as x ([User_ID], [Date], [SMS])
GO
;WITH MX as (
SELECT t1.[User_ID], MIN(t1.[Date]) as [Date], t1.[SMS]
FROM Table1 as t1
WHERE t1.[SMS] = (SELECT MAX(i.[SMS]) as [SMS] FROM Table1 as i WHERE i.[User_ID] = t1.[User_ID] )
GROUP BY t1.[User_ID], t1.[SMS]
), MI as (
SELECT t1.[User_ID], MAX(t1.[Date]) as [Date], t1.[SMS]
FROM Table1 as t1
WHERE t1.[SMS] = (SELECT MIN(i.[SMS]) as [SMS] FROM Table1 as i WHERE i.[User_ID] = t1.[User_ID] )
GROUP BY t1.[User_ID], t1.[SMS]
)
SELECT MX.[User_ID], MX.[Date] as Min_Date, MX.[SMS] as Max_SMS, MI.[Date] as Max_Date, MI.[SMS] as Min_SMS
FROM MX INNER JOIN MI ON MX.[User_ID] = MI.[User_ID];
GO
I need some help structuring a query to only pull back recurrences that are after a set number of days, in my case 30.
My table structure is as follows:
PatientID Date
1 2015-09-01
1 2015-09-03
2 2015-03-04
2 2015-03-07
2 2015-09-15
In this example, I only want to return rows 1, 3, and 5.
I tried doing a left join on itself, where the date in the second is > DATEADD(D,30,Date).
My other thought was a recursive CTE with the first query pulling the min date for each patient then a union where the table date was at least 30 days greater than the max of each patients CTE date but you can't have a max in the join statement.
I'm pretty stumped. Any advice would be greatly appreciated.
This is how I would do it:
SELECT * FROM MyTable t1
WHERE NOT EXISTS(
SELECT * FROM MyTable t2
WHERE t1.PatientId=t2.PatientId
AND t2.Date < t1.Date
AND DATEDIFF(dd, t2.Date, t1.Date) < 30
)
ORDER BY t1.PatientId, t1.Date ASC
I think something like this should work (notepad coding here, so the syntax may be a little off)
WITH CTE(
SELECT PatientId, Min(Date) as Date
FROM MyTable
Group BY PatientId)
SELECT A.*
FROM MyTable A
LEFT OUTER JOIN CTE CTE
ON A.PatientId = CTE.PatientId
AND (A.Date = CTE.Date OR A.Date > DATEAdd(dd, 30, CTE.Date)
WHERE CTE.PatientId IS NOT NULL
I am looking to merge the following two queries into one:
select top 100 date, count(*) from sections
where content not like '%some condition%'
group by date
order by date;
select top 100 date, count(*) from sections
group by date
order by date;
It is like this question, LEFT JOIN after GROUP BY? except that I need this to work for MS SQL Server, not MySQL (the difference being that MSSQL does not allow subqueries in the from clause).
I am looking for a way to have the result set have three columns, date, the first count(*), and the second count(*).
My current solution is:
select top 100 date,
(select count(*) from sections s1
where content not like '%some condition%'
and s1.date = s2.date),
(select count(*) from sections s1
where s1.date=s2.date) from sections as s2
group by date
order by date;
Is there a better way to do this?
Try this:
with q1 as (select top 100 date, count(*) total from sections
where content not like '%some condition%'
group by date),
q2 as (select top 100 date, count(*) total from sections
group by date)
select q1.date, q1.total total1, q2.total total2
from q1
join q2 on q1.date = q2.date
order by q1.date
UPDATE:
Or this:
select date,
count(*) total,
sum(has_condition) total_condition
from (select top 100
date,
case when content not like '%some condition%' then 1
else 0 end has_condition
from sections ) t
group by date
order by date;
I did not do any triout, but that is the idea.
This is the query that do the job with just one select:
select top 100 date,
count(*) as count_all,
sum (
case
when content not like '%some condition%' then 1
else 0
end
) as count_condition
from sections
group by date
order by date
I am also pasting a working snippet from AdventureWorks2012 database
select top 100
ModifiedDate,
count(*) as count_all,
sum (
case when CarrierTrackingNumber not like '4911%' then 1
else 0
end
) as count_condition
from [Sales].[SalesOrderDetail]
group by ModifiedDate
order by ModifiedDate
For you reference you can use subqueries after FROM clause in SQL Server.
I have a table t1 with two int fields(id,month) and I have populated it with some values.
What I would like to see as an output is, the maximum of (count of id in a month). I have tried the following code and it works fine:
select id,max(freq) as maxfreq from
(select id,month,count(*) as freq
from t1
group by id,month) a
group by id
order by maxfreq desc
The result is:
ID MAXFREQ
1 3
2 3
3 1
4 1
This is fine. How to achieve this using the over partition by clause? And which one is more efficient? In reality my table consists of several thousands of records. So doing a subquery wont be a good idea I guess! Thanks for any help. Here's the fiddle
;WITH tmp AS (select id, row_number() over (partition by id, month order by id) rn
FROM t1)
SELECT t.id, max(tmp.rn) as maxfreq
from t1 t
INNER JOIN tmp ON tmp.id = t.id
GROUP BY t.id
You can try this -
select id,max(freq) as maxfreq from
(select id,row_number() over (partition by id,month ORDER BY id) as freq
from t1
) a
group by id
order by id,maxfreq desc
but from a performance standpoint, I do not see much difference between your original query and this one.
Same solution but with using CTE.
Actually there is no point to forcibly use windowing functions to this issue.
Compare both solutions with plan explorer.
;with c1 as
( select id,month,count(*) as freq
from t1
group by id,month
)
select id, max(freq) as maxfreq
from c1
group by id
order by maxfreq desc;