SQL over partition count then group by month - sql

I've been playing around with using COUNT(value) OVER (PARTITION BY anothervalue) and I love how fast it is pulling results from my db. I'm trying to step things up now so that I can create a table that will show me multiple counts - one for each month, side by side. I'm not sure if it is possible but wanted to check here. I had a look around online but couldn't find any examples of the question having been asked before, so no leads.
Here is what I currently have:
SELECT DISTINCT
TBL.CATEGORY as 'Category Name',
COUNT(TBL.ROW_ID) OVER (PARTITION BY TBL.CATEGORY) as 'Rows in Category'
FROM
MYDB.TBL
WHERE
TBL.FIELD1 = 'something'
AND TBL.FIELD2 = 'somethingelse'
AND TBL.CREATED >= '2014-01-01'
ORDER BY [Rows in Category] desc
Gives me a lovely table like this:
Category Name |Rows in Category
ABC | 166
CBA | 137
CCC | 112
Where I'm trying to get now is to subdivide by month so my output ends up looking something like this (does not have to be exact, the headers can be shuffled around):
JANUARY |FEBRUARY
Category Name |Rows in Category | Category Name |Rows in Category
ABC | 162 | CBA | 51
CBA | 86 | CCC | 32
CCC | 70 | ABC | 4
When I try adding GROUP BY it throws an error about something not being contained in an aggregate function.
If I have to I can just stack queries on top of each other and limit each one to only show one month, but that seems like a lot of repetitive code and I'd prefer a side by side view if it can be done.
Any ideas?

The format will not be exactly as you described. But this would be a normal way to show the result and all information has been included, make sure you do not display more than 1 year at a time.
Try this:
;WITH x AS
(
SELECT TBL.CATEGORY, TBL.ROW_ID, datename(month, TBL.CREATED) month
FROM
MYDB.TBL
WHERE
TBL.FIELD1 = 'something'
AND TBL.FIELD2 = 'somethingelse'
AND TBL.CREATED >= '2014-01-01'
AND TBL.CREATED < '2015-01-01'
)
SELECT CATEGORY as 'Category Name', [January],[February]
FROM x
PIVOT(
count([ROW_ID])
FOR month
in([January],[February])
)AS p

#t-clausen.dk thanks for your help with this. I had not previously used coalesce for anything so I learned something useful today. In the end, since I it turns out I need a total column anyway, I added a YTD column and was able to order by that.
Wanted to post the final result in case it is of use to anyone else:
WITH X AS
(
SELECT
tbl.category,
tbl.row_id,
DATENAME(MONTH, tbl.created) MONTH,
COUNT(tbl.row_id) OVER (PARTITION BY tbl.category) AS 'YTD'
FROM
db.tbl
WHERE
tbl.randomcolumn = 'something specific'
AND tbl.anothercolumn = 'something else specific'
AND created >= '2014-01-01'
AND created < '2015-01-01'
)
SELECT
category AS 'Category Name',
[January], [February], [March], [April], [May], [June], [July], [August], [September], [October], [November], [December],
[YTD]
FROM X
PIVOT
(COUNT([row_id])
FOR MONTH
in([January], [February], [March], [April], [May], [June], [July], [August], [September], [October], [November], [December])
)AS P
ORDER BY [YTD] DESC
Switch out the lower case parts for the equivalents in your own db/table if looking to repurpose this query.

Related

Choosing MAX value by id in a view?

I have created a simple view based on a few columns in our database
ALTER VIEW [BI].[v_RCVLI_Test] AS
Select distinct
Borger.CPRnrKort as CPR,
(...)
IndsatsDetaljer.VisitationId as VisitationsId,
Indsats.KatalogNavn as IndsatsNavn,
(case
when
(
Indsats.Model = 'SMDB2 Tilbudsmodel' or
Indsats.Model = 'SMDB2 Samtalemodel' or
Indsats.Model = 'Tilbudsmodel' or
Indsats.Model = 'NAB Tilbudsmodel'
)
then IndsatsDetaljer.ServicePeriodeStart
else IndsatsDetaljer.Ikrafttraedelsesdato
end
) as StartDato,
(case
when
(
Indsats.Model = 'SMDB2 Tilbudsmodel' or
Indsats.Model = 'SMDB2 Samtalemodel' or
Indsats.Model = 'Tilbudsmodel'
)
then (case when IndsatsDetaljer.VisitationSlut = '9999-12-31' then convert(varchar(10), getdate(), 23) else IndsatsDetaljer.VisitationSlut end)
when
Indsats.Model = 'NAB Tilbudsmodel'
then (case when IndsatsDetaljer.NABehandlingSlutDato = '9999-12-31' then convert(varchar(10), getdate(), 23) else IndsatsDetaljer.NABehandlingSlutDato end)
else (case when IndsatsDetaljer.VisitationSlut = '9999-12-31' then convert(varchar(10), getdate(), 23) else IndsatsDetaljer.VisitationSlut end)
end
) as StopDato,
Refusion.Handlekommune as Handlekommune,
replace(Refusion.Betalingskommune, 'Ukendt', 'Kendt') Betalingskommune
from nexus2.Fact_VisiteretTid as Fact
join nexus2.Dim_Paragraf Paragraf
on Fact.DW_SK_Paragraf = Paragraf.DW_SK_Paragraf
join nexus2.Dim_Indsats Indsats
on Fact.DW_SK_Indsats = Indsats.DW_SK_Indsats (...)
The cases for StartDato and StopDato are there because those dates come from different columns. I've converted the date '9999-12-31' to the the current date because we'll be doing some time calculations later on, and it's just more convenient.
CPR is the id of a person, VisitationsId is the id for the service the person received.
In theory, There should only be one StartDato and one StopDato per VisitationsId, but because of a glitch in the documentation system, we sometimes get TWO StopDato: one is the correct, and one is '9999-12-31' (now converted to current date).
So I need to group by VisitationsId and then just take the MIN value of StopDato, but I'm kind of unsure how to go about doing that?
CPR
VisitationsId
StartDato
StopDato
Something Else
123
56
2019-01-01
2019-12-12
Something
123
56
2019-01-01
9999-12-31
Something
123
58
2019-01-01
2019-12-14
Something
345
59
2018-11-01
9999-12-31
Something
345
55
2017-01-02
2017-11-12
Something
345
55
2017-01-02
9999-12-31
Something
In the above table I need to remove lines 2 and 6, because the VisitationsId is identical to the previous row, but they diverge on StopDato.
Using a group by anywhere in the query gives me an error on another (seemingly random) column telling me that the column is:
invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Any suggestions on how I can go about doing this?
Add a filter which tests for this condition?
with cte (
{your current query}
)
select *
from cte T
where not (
StopDato = '9999-12-31'
and exists (
select 1
from cte T1
where T1.VisitationsId = T.VisitationsId
and StopDato != '9999-12-31'
)
);
And you look like you are converting StopDato to a varchar which is bad - you should treat dates as dates until you need to display them.

CASE condition and SUM() common table expressions or Group By

Hope some can help ? I have a 3rd party software were i can do custom sql querys but not able to change any of the database. Struggling to get the result i would like.
Do I use GROUP BY or CTE to get the result i need?
WITH CTE AS (
SELECT tblTicketsSummary.fldDateScheduled AS [Scheduled],
tblTicketsRow.fldStartTime AS [TIME], tblTicketsRow.fldPs AS
[s],tblTicketsRow.fldPrice AS PR,
tblTicketsSummary.fldFirstName+' '+tblTicketsSummary.fldLastName
AS [Client], ROW_NUMBER() OVER
(PARTITION BY tblTicketsSummary.fldTicketID ORDER BY
tblTicketsRow.fldTicketID) AS C, tblTicketsSummary.fldDateClosed
AS [Date Closed],
CASE WHEN tblTicketsRow.fldPs ='p' THEN tblTicketsRow.fldPrice
ELSE £0.00 END AS Product,
CASE WHEN tblTicketsRow.fldPs ='s' THEN tblTicketsRow.fldPrice
ELSE £0.00 END AS Services,
FROM tblTicketsSummary INNER JOIN
tblTicketsRow ON tblTicketsSummary.fldTicketID = tblTicketsRow.fldTicketID
WHERE (fldDateVoided IS NULL) AND (NOT(fldDateClosed IS NULL))
GetUserDate('AND','fldDateClosed','') AND tblTicketsRow.fldEmployeeName ='Tina Young'
AND tblTicketsSummary.fldTotal >1 )
SELECT * FROM CTE ORDER BY Scheduled ASC, TIME ASC
This give me the result below . I have removed Where c=1 in the last select which i will put back in the final query which give me the first result by time which is what i want. The bit i am struggling with is. I would like to add the prices of all products together and the prices of all service together to give a total of products and services sold. Then a grand total of services and product together in the final query .
Scheduled TIME s PR Client C Date Closed Product Services Total
17/07/2020 17:00 S £10.00 Ben Preston 1 17/07/2020 £0.00 £10.00 s+p
17/07/2020 17:45 S £1.00 Ben Preston 2 17/07/2020 £0.00 £1.00 s+p
17/07/2020 P £19.00 Ben Preston 3 17/07/2020 £19.00 £0.00 s+p
17/07/2020 P £10.00 Ben Preston 4 17/07/2020 £10.00 £0.00 s+p
The Result i would like is below. The final part of the query is as below. I am only able to custom query's in the 3rd party software.
SELECT * FROM CTE WHERE c=1 ORDER BY Scheduled ASC, TIME ASC
Scheduled TIME Client C Date Closed Product Services Total
17/07/2020 17:00 Ben Preston 1 17/07/2020 £29.00 £11.00 £40.00
I hope I have formatted this right for people to understand. Thank you for any help you can provide.
Seems like you simply need to add two Group Sums to your existing CTE and then add them in your final Select:
WITH CTE AS (
SELECT tblTicketsSummary.fldDateScheduled AS [Scheduled],
tblTicketsRow.fldStartTime AS [TIME],
tblTicketsRow.fldPs AS [s],tblTicketsRow.fldPrice AS PR,
tblTicketsSummary.fldFirstName+' '+tblTicketsSummary.fldLastName AS [Client],
ROW_NUMBER()
OVER (PARTITION BY tblTicketsSummary.fldTicketID
ORDER BY tblTicketsRow.fldStartTime) AS C,
tblTicketsSummary.fldDateClosed AS [Date Closed],
SUM(CASE WHEN tblTicketsRow.fldPs ='p' THEN tblTicketsRow.fldPrice ELSE £0.00 END)
OVER (PARTITION BY tblTicketsSummary.fldTicketID) AS Product,
SUM(CASE WHEN tblTicketsRow.fldPs ='s' THEN tblTicketsRow.fldPrice ELSE £0.00 END)
OVER (PARTITION BY tblTicketsSummary.fldTicketID) AS Services
FROM tblTicketsSummary
INNER JOIN tblTicketsRow
ON tblTicketsSummary.fldTicketID = tblTicketsRow.fldTicketID
WHERE (fldDateVoided IS NULL) AND (NOT(fldDateClosed IS NULL))
--GetUserDate('AND','fldDateClosed','') AND tblTicketsRow.fldEmployeeName ='Tina Young'
AND tblTicketsSummary.fldTotal >1 )
SELECT cte.*,
Product + Services as total
FROM CTE
WHERE C = 1
ORDER BY Scheduled ASC, TIME ASC;
Btw, the ORDER BY tblTicketsRow.fldTicketID on your ROW_NUMBER is not based on a unique column, thus the order of rows is not guaranteed. You probably want order by tblTicketsRow.fldStartTime instead.

Datediff on 2 rows of a table with a condition

My data looks like the following
TicketID OwnedbyTeamT Createddate ClosedDate
1234 A
1234 A 01/01/2019 01/05/2019
1234 A 10/05/2018 10/07/2018
1234 B 10/04/2019 10/08/2018
1234 finance 11/01/2018 11/11/2018
1234 B 12/02/2018
Now, I want to calculate the datediff between the closeddates for teams A, and B, if the max closeddate for team A is greater than max closeddate team B. If it is smaller or null I don't want to see them. So, for example,I want to see only one record like this :
TicketID (Datediff)result-days
1234 86
and for another tickets, display the info. For example, if the conditions aren't met then:
TicketID (Datediff)result-days
2456 -1111111
Data sample for 2456:
TicketID OwnedbyTeamT Createddate ClosedDate
2456 A
2456 A 10/01/2019 10/05/2019
2456 B 08/05/2018 08/07/2018
2456 B 06/04/2019 06/08/2018
2456 finance 11/01/2018 11/11/2018
2456 B 12/02/2018
I want to see the difference in days between 01/05/2019 for team A, and
10/08/2018 for team B.
Here is the query that I wrote, however, all I see is -1111111, any help please?:
SELECT A.incidentid,
( CASE
WHEN Max(B.[build validation]) <> 'No data'
AND Max(A.crfs) <> 'No data'
AND Max(B.[build validation]) < Max(A.crfs) THEN
Datediff(day, Max(B.[build validation]), Max(A.crfs))
ELSE -1111111
END ) AS 'Days-CRF-diff'
FROM (SELECT DISTINCT incidentid,
Iif(( ownedbyteam = 'B'
AND titlet LIKE '%Build validation%' ), Cast(
closeddatetimet AS NVARCHAR(255)), 'No data') AS
'Build Validation'
FROM incidentticketspecifics) B
INNER JOIN (SELECT incidentid,
Iif(( ownedbyteamt = 'B'
OR ownedbyteamt =
'Finance' ),
Cast(
closeddatetimet AS NVARCHAR(255)), 'No data') AS
'CRFS'
FROM incidentticketspecifics
GROUP BY incidentid,
ownedbyteamt,
closeddatetimet) CRF
ON A.incidentid = B.incidentid
GROUP BY A.incidentid
I hope the following answer will be of help.
With two subqueries for the two teams (A and B), the max date for every Ticket is brought. A left join between these two tables is performed to have these information in the same row in order to perform DATEDIFF. The last WHERE clause keeps the row with the dates greater for A team than team B.
Please change [YourDB] and [MytableName] in the following code with your names.
--Select the items to be viewed in the final view along with the difference in days
SELECT A.[TicketID],A.[OwnedbyTeamT], A.[Max_DateA],B.[OwnedbyTeamT], B.[Max_DateB], DATEDIFF(dd,B.[Max_DateB],A.[Max_DateA]) AS My_Diff
FROM
(
--The following subquery creates a table A with the max date for every project for team A
SELECT [TicketID]
,[OwnedbyTeamT]
,MAX([ClosedDate]) AS Max_DateA
FROM [YourDB].[dbo].[MytableName]
GROUP BY [TicketID],[OwnedbyTeamT]
HAVING [OwnedbyTeamT]='A')A
--A join between view A and B to bring the max dates for every project
LEFT JOIN (
--The max date for every project for team B
SELECT [TicketID]
,[OwnedbyTeamT]
,MAX([ClosedDate]) AS Max_DateB
FROM [YourDB].[dbo].[MytableName]
GROUP BY [TicketID],[OwnedbyTeamT]
HAVING [OwnedbyTeamT]='B')B
ON A.[TicketID]=B.[TicketID]
--Fill out the rows on the max dates for the teams
WHERE A.Max_DateA>B.Max_DateB
You might be able to do with a PIVOT. I am leaving a working example.
SELECT [TicketID], "A", "B", DATEDIFF(dd,"B","A") AS My_Date_Diff
FROM
(
SELECT [TicketID],[OwnedbyTeamT],MAX([ClosedDate]) AS My_Max
FROM [YourDB].[dbo].[MytableName]
GROUP BY [TicketID],[OwnedbyTeamT]
)Temp
PIVOT
(
MAX(My_Max)
FOR Temp.[OwnedbyTeamT] in ("A","B")
)PIV
WHERE "A">"B"
Your sample query is quite complicated and has conditions not mentioned in the text. It doesn't really help.
I want to calculate the datediff between the closeddates for teams A, and B, if the max closeddate for team A is greater than max closeddate team B. If it is smaller or null I don't want to see them.
I think you want this per TicketId. You can do this using conditional aggregation:
SELECT TicketId,
DATEDIFF(day,
MAX(CASE WHEN OwnedbyTeamT = 'B' THEN ClosedDate END),
MAX(CASE WHEN OwnedbyTeamT = 'A' THEN ClosedDate END) as diff
)
FROM incidentticketspecifics its
GROUP BY TicketId
HAVING MAX(CASE WHEN OwnedbyTeamT = 'A' THEN ClosedDate END) >
MAX(CASE WHEN OwnedbyTeamT = 'B' THEN ClosedDate END)

SQL Server 2008 Running Total

I'm aware this has been asked but I'm completely baffled.
Trying to run a running total by day using SQL Server 2008. Have looked at solutions elsewhere but would am still completely perplexed.
The below code shows Daily sales but I cannot make a running total fit. Have looked at the similar solutions here but no luck. Have looked at partition by, order by, CTE etc but I'm just not there yet with SQL.
Would appreciate help, my code is below. I know this only returns the total grouped by day...
SELECT
dim_invoice_date.invoice_date AS 'Invoice Date',
round(SUM(invoice_amount_corp),2) AS 'Sales'
FROM
fact_om_bud_invoice
JOIN
dim_invoice_date ON fact_om_bud_invoice.dim_invoice_date_key = dim_invoice_date.dim_invoice_date_key
WHERE
dim_invoice_date.current_cal_month IN ('Current')
AND fact_om_bud_invoice.budget_code IN ('BUDGET')
GROUP BY
dim_invoice_date.invoice_date
HAVING
ROUND(SUM(invoice_amount_corp), 2) <> 0
ORDER BY
'Invoice Date'
This returns the output:
Invoice Date Sales
-----------------------
4/10/2016 24,132
5/10/2016 15,849
6/10/2016 24,481
7/10/2016 10,243
10/10/2016 42,398
11/10/2016 24,187
Required format is something like:
Invoice Date Sales Running Sales
-------------------------------------------
04/10/2016 24,132 24,132
05/10/2016 15,849 39,981
06/10/2016 24,481 64,462
07/10/2016 10,243 74,705
10/10/2016 42,398 117,103
11/10/2016 24,187 141,290
dim_invoice_date is a numeric field, it's looking up a separate date table to display as date time.
For example, can use WITH common_table_expression
WITH cte AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY h.[Date]) RowN,
h.[Date],
SUM(s.Quantity) q
FROM
Sales s
JOIN Headers h
ON s.ID_Headers = h.ID
WHERE
h.[Date] > '2016.10.31'
GROUP BY
h.[Date]
)
SELECT
c.[Date],
c.q,
SUM(c1.q)
FROM
cte c
JOIN cte c1
ON c1.RowN <= c.RowN
GROUP BY
C.[Date],
c.q
ORDER BY
c.[Date]

SQL Sum(Count) of specific columns and group SUM by Month

So i have a bunch of data, and i already have it grouped by The column name and month.
Here is the SQL query i have so far
TestName is a column name, POE Business Rules/Submit occur many times per column
VExecutionGlobalHistory is the name of the table,
Im using Microsoft SQL Server Management Studio 2010
select
year(dateadd(mm,datediff(mm,0,StartTime),0)),
datename(month,dateadd(mm,datediff(mm,0,StartTime),0)),TestName,
Case WHEN Testname = 'POE Business Rules' THEN (count(TestName)*36) WHEN TestName = 'Submit' THEN (count(TestName)*6) ELSE 0 END
From VExecutionGlobalHistory
group by
year(dateadd(mm,datediff(mm,0,StartTime),0)),
datename(month,dateadd(mm,datediff(mm,0,StartTime),0)),TestName
This query gives me this format
2013 |APRIL| POE Business Rules| 1044
2013 |APRIL| SUBMIT | 96
2013 |JULY | POE Business Rules| 216
2013 |JULY | SUBMIT | 102
I would like to have a final format where it has only each month with the sum of the counts
2013|APRIL|SUM of the counts or (1044 + 96)
2013|JULY |SUM of the counts or (216 + 102)
I dont need the testname just the sum of the counts per month
I have tried adding SUM right before case but i get
"Cannot perform an aggregate function on an expression containing an aggregate or a subquery." Error.
Any suggestions on another approach?
You could just use a subquery:
SELECT Year_, Month_, SUM(Counts)
FROM (
SELECT YEAR(DATEADD(MM,DATEDIFF(MM,0,StartTime),0))'Year_'
,DATENAME(MONTH,DATEADD(MM,DATEDIFF(MM,0,StartTime),0))'Month_'
,TestName
,CASE WHEN Testname = 'POE Business Rules' THEN (count(TestName)*36)
WHEN TestName = 'Submit' THEN (COUNT(TestName)*6)
ELSE 0
END 'Counts'
FROM VExecutionGlobalHistory
GROUP BY YEAR(DATEADD(MM,DATEDIFF(MM,0,StartTime),0))
,DATENAME(MONTH,DATEADD(MM,DATEDIFF(MM,0,StartTime),0))
,TestName
)sub
GROUP BY Year_, Month_
ORDER BY CAST(CAST(Year_ AS CHAR(4)) + Month_ + '01' AS DATETIME)
Update: Added ORDER BY to sort by YEAR/MONTH oldest first.
Something like
Select SumYear,SomeMonth,Sum(SumCounts) From
(
select
year(dateadd(mm,datediff(mm,0,StartTime),0)) as SumYear,
datename(month,dateadd(mm,datediff(mm,0,StartTime),0)) as SumMonth,TestName,
Case WHEN Testname = 'POE Business Rules' THEN (count(TestName)*36) WHEN TestName = 'Submit' THEN (count(TestName)*6) ELSE 0 END as sumCounts
From VExecutionGlobalHistory
group by
year(dateadd(mm,datediff(mm,0,StartTime),0)),
datename(month,dateadd(mm,datediff(mm,0,StartTime),0)),TestName ) sums
Group by SumYear,SumMonth
should do it
Your logic may be overcomplicated. It sounds like you just want a weighted sum grouped by (year,month). Rows with 'POE Business Rules' are worth 36, rows with 'Submit' are worth 6, and all others are worth 0.
SELECT
YEAR(StartTime),
DATENAME(month,DATEADD(month,MONTH(StartTime)-1,0)),
SUM(
CASE Testname
WHEN 'POE Business Rules' THEN 36
WHEN 'Submit' THEN 6
ELSE 0
END
)
FROM VExecutionGlobalHistory
GROUP BY YEAR(StartTime),MONTH(StartTime)
ORDER BY YEAR(StartTime),MONTH(StartTime)