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

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.

Related

How to Count the number of times of purchases per customer in a Month?

I would like to know how many times a customer bought per month since the store has been opened.
I have this code:
SELECT
Descrip Customer_Name
, CodClie ID_Customer
, CASE WHEN TipoFac = 'A' THEN Monto
ELSE -Monto END AS Purchase_Amount$
, CONVERT(varchar, FechaE, 1) AS Date
, CodUbic Store_Location
, DENSE_RANK() OVER ( partition by Descrip order by NumeroD) +
DENSE_RANK() OVER (partition by Descrip order by NumeroD desc) -1 as Times_bought_since_1st_Day
FROM
dbo.SAFACT
WHERE
TipoFac IN ('A', 'B')
GROUP BY
FechaE --- Date in timestamp, that's why I converted it
, CodUbic --- Stores Location
, Monto --- Amount spent $
, Descrip --- Customer Name
, TipoFac --- A for purchases, B for returned products, that why I make the Case Statement
, CodClie --- ID Customer
, NumeroD --- NumeroD = Bill
ORDER BY YEAR(FechaE) DESC, MONTH(FechaE) DESC, DAY(FechaE) DESC;
If I run this code, It will show something like this:
Customer_Name ID_Customer Purchase_Amount$ Date Store_Location Times_bought_since_1st_Day
------------------------------------------------------------------------------------------------
Peter Grifin 12345678 1.5 06/14/2022 000-1 10
Ralph Lauren 89101112 2.0 06/14/2022 000-4 15
Meg Stacy 13141516 4.0 06/14/2022 000-5 10
The issue here is that I need the times bought but per month, even if It is possible make a group by per every customer's name and their amount spent together with the times bought
The expected result would be something like:
Customer_Name ID_Customer Purchase_Amount$ Date StoreLocation TimesBoughtSince1stDay TimesBoughtCurrentMonth TotalSpentSince1stDay TotalSpentThisMonth
------------------------------------------------------------------------------------------------------------------------------------------------------------
Peter Grifin 12345678 1.5 06/14/2022 000-1 10 20 100.000 1.000
Ralph Lauren 89101112 2.0 06/14/2022 000-4 15 05 25.000 500.00
Meg Stacy 13141516 4.0 06/14/2022 000-5 18 10 15.000 200.00
If there any possibility to split the times bought per month?
I think that you are confusing because you want to mix calculations within differents times aggregations in one table from the beggining. If you think the problem like 3 different problems and then mixing them, it will be easier.
Try to calculate in different tables by time aggregation and then joining the tables.
The first 5 columns of what you want to achieve are transactional data with an aggregation of days. Every row is a sale made one specific day, a customer can do 1 or more purchases every day.
Then, with the columns TimesBoughtSince1stDay and TotalSpentSince1stDay you don´t need to aggregate by date. You need to aggregate by Customer entire history and you can achieve that by doing:
SELECT
Descrip Customer_Name
,CodClie ID_Customer
,sum(CASE WHEN TipoFac = 'A' THEN Monto ELSE -Monto END) 'Purchase_Amount$'
, count( DISTINCT NumeroD ) -- here I don´t understand that much your code but you need to count differents SaleId's
FROM dbo.SAFACT
WHERE TipoFac IN ('A', 'B')
GROUP BY Descrip Customer_Name, CodClie ID_Customer
For the month time aggregation you can do someting like:
SELECT
Descrip Customer_Name
, CodClie ID_Customer
, sum(CASE WHEN TipoFac = 'A' THEN Monto
ELSE -Monto END) 'Purchase_Amount$'
, count( DISTINCT NumeroD ) -- here I don´t understand that much your code but you need to count differents SaleId's
FROM
dbo.SAFACT
WHERE
TipoFac IN ('A', 'B')
AND MONTH(CONVERT(DATE,FechaE)) = MONTH(GETDATE())
AND YEAR(CONVERT(DATE,FechaE)) = YEAR(GETDATE())
GROUP BY Descrip Customer_Name, CodClie ID_Customer, MONTH(CONVERT(DATE,FechaE))
Once you have your 3 differents tables(aggregation by date, aggregation by customer history and aggregation by month) you need to do a left join over the "aggregation by date" table.
Surely some of the code isn´t 100% ready to run but i wanted you to catch the main idea. Hope it will help!

Need to pull customers with specific activity this year (Leads), where that activity (Lead) was their last one

I'm using SSMS to create a report showing customer accounts where the Sales Reps didn't follow up on leads we received this year. That would be indicated in accounts wheriin the list of activities (actions in the account), 'Lead' is the last one listed (the rep didn't take any actions after receiving the lead).
My code is pulling the latest 'Lead' activity for all customers who've had at least one lead this year:
CustomerName
Activity
Date
Bob's Tires
Lead
2021-01-05
Ned's Nails
Lead
2021-02-02
Good Eats
Lead
2021-02-03
I need it to only pull customers where the Lead was the last activity:
CustomerName
Activity
Date
Ned's Nails
Lead
2021-02-02
Here is my code and example tables. What am I missing? I've tried many things with no luck.
WITH activities AS (
SELECT
a. *
, CASE WHEN a.ContactDate = MAX(CASE WHEN a.Activity LIKE 'Lead%'
THEN a.ContactDate END) OVER (PARTITION BY a.AcctID)
THEN 1 ELSE 0 END AS no_followup
FROM AcctActivities a
WHERE a.ContactDate >= '2021-01-01'
)
SELECT
c.Name,
act.Activity,
act.ContactDate
FROM Customers c
INNER JOIN activities act ON c.AcctID = act.AcctID AND act.no_followup = 1
ORDER BY c.AcctID, act.ContactDate ASC
Table 1: Customers (c)
AcctID
CustomerName
11
Bob's Tires
12
Ned's Nails
13
Good Eats
14
Embers
Table 2: Activities (a)
AcctivityID
AcctID
Activity
Date
1
11
Contact Added
2021-01-01
2
11
Lead
2021-01-05
3
11
Phone Call
2021-01-06
4
12
Lead
2021-02-02
5
13
Lead
2021-02-03
6
13
Phone Call
2021-01-15
7
13
Sales Email
2021-01-15
8
14
Cold Call
2021-01-20
Your approach filters which rows may be considered in the max value in the comparison. I've included the suggested modification below which also modifies your CASE expression to consider that the current row is a lead as the case expression may filter the bounded values to consider (i.e. it will give you the latest lead activity but the latest lead activity may not be your latest activity).
Another modification, possibly optional but safe is adding the ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING in the OVER clause of your partition. While you could have also used UNBOUNDED PRECEDING instead of CURRENT ROW, it seems like extra processing when all the rows before the ordered ContactDate would be already be less than the current value and you are interested in the maximum value for contact date . The window function by default considers all rows current and before. The amendment would ask the window function to look at all the results in the partition after the current row.
Eg.
WITH activities AS
(
SELECT
a. *,
CASE
WHEN a.Activity LIKE 'Lead%' AND
a.ContactDate = (MAX(a.ContactDate) OVER (PARTITION BY a.AcctID ORDER BY a.ContactDate ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ))
THEN 1
ELSE 0
END AS no_followup
FROM
AcctActivities a
WHERE
a.ContactDate >= '2021-01-01'
)
SELECT
c.Name,
act.Activity,
act.ContactDate
FROM
Customers c
INNER JOIN
activities act ON c.AcctID = act.AcctID
AND act.no_followup = 1
ORDER BY
c.AcctID, act.ContactDate ASC
Furthermore, if you are only interested in the customer details and all the resulting activity names would be Lead you may consider the following approach which uses aggregation and having clause to filter your desired results. This approach returns less details in the resulting CTE by filtering early.
WITH customers_with_last_activity_as_lead as (
SELECT AcctID, MAX(ContactDate) as ContactDate
FROM AcctActivities a
WHERE a.ContactDate >= '2021-01-01'
GROUP BY AcctID
HAVING
MAX(a.ContactDate) = MAX(
CASE
WHEN a.Activity LIKE 'Lead%' THEN a.ContactDate
END
)
)
SELECT
c.Name,
-- 'Lead' as Activity, -- Uncomment this line if it is that you would like to see this constant value in your resulting queries.
act.ContactDate
FROM
Customers c
INNER JOIN
customers_with_last_activity_as_lead act ON c.AcctID = act.AcctID
ORDER BY
c.AcctID, act.ContactDate ASC
if all the values aren't a constant/literal Lead then the following approach may assist in retrieving the correct activity name also
WITH customers_with_last_activity_as_lead as (
SELECT
AcctID,
REPLACE(MAX(CONCAT(ContactDate,Activity)),MAX(ContactDate),'') as Activity,
MAX(ContactDate) as ContactDate
FROM AcctActivities a
WHERE a.ContactDate >= '2021-01-01'
GROUP BY AcctID
HAVING
MAX(a.ContactDate) = MAX(
CASE
WHEN a.Activity LIKE 'Lead%' THEN a.ContactDate
END
)
)
SELECT
c.Name,
act.Activity,
act.ContactDate
FROM
Customers c
INNER JOIN
customers_with_last_activity_as_lead act ON c.AcctID = act.AcctID
ORDER BY
c.AcctID, act.ContactDate ASC
Let me know if this works for you.
I agree with Gordon that your question isn't totally clear (do you actually only care about activities this year? What is the intended meaning of no_followup?). Having said that, I think this is what you want:
select c.Name,
lastActivity.Activity,
lastActivity.ContactDate
from Customers c
cross apply (
select top 1 a.Activity, a.ContactDate
from activities a
where a.acctId = c.acctId
-- and a.ContactDate >= '2021-01-01' uncomment this if you only care about activity this year
order by a.ContactDate desc
) lastActivity
where lastActivity.Activity like 'Lead%'

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 query to identify seasonal sales items

I need a SQL query that will identify seasonal sales items.
My table has the following structure -
ProdId WeekEnd Sales
234 23/04/09 543.23
234 30/04/09 12.43
432 23/04/09 0.00
etc
I need a SQL query that will return all ProdId's that have 26 weeks consecutive 0 sales. I am running SQL server 2005. Many thanks!
Update: A colleague has suggested a solution using rank() - I'm looking at it now...
Here's my version:
DECLARE #NumWeeks int
SET #NumWeeks = 26
SELECT s1.ProdID, s1.WeekEnd, COUNT(*) AS ZeroCount
FROM Sales s1
INNER JOIN Sales s2
ON s2.ProdID = s1.ProdID
AND s2.WeekEnd >= s1.WeekEnd
AND s2.WeekEnd <= DATEADD(WEEK, #NumWeeks + 1, s1.WeekEnd)
WHERE s1.Sales > 0
GROUP BY s1.ProdID, s1.WeekEnd
HAVING COUNT(*) >= #NumWeeks
Now, this is making a critical assumption, namely that there are no duplicate entries (only 1 per product per week) and that new data is actually entered every week. With these assumptions taken into account, if we look at the 27 weeks after a non-zero sales week and find that there were 26 total weeks with zero sales, then we can deduce logically that they had to be 26 consecutive weeks.
Note that this will ignore products that had zero sales from the start; there has to be a non-zero week to anchor it. If you want to include products that had no sales since the beginning, then add the following line after `WHERE s1.Sales > 0':
OR s1.WeekEnd = (SELECT MIN(WeekEnd) FROM Sales WHERE ProdID = s1.ProdID)
This will slow the query down a lot but guarantees that the first week of "recorded" sales will always be taken into account.
SELECT DISTINCT
s1.ProdId
FROM (
SELECT
ProdId,
ROW_NUMBER() OVER (PARTITION BY ProdId ORDER BY WeekEnd) AS rownum,
WeekEnd
FROM Sales
WHERE Sales <> 0
) s1
INNER JOIN (
SELECT
ProdId,
ROW_NUMBER() OVER (PARTITION BY ProdId ORDER BY WeekEnd) AS rownum,
WeekEnd
FROM Sales
WHERE Sales <> 0
) s2
ON s1.ProdId = s2.ProdId
AND s1.rownum + 1 = s2.rownum
AND DateAdd(WEEK, 26, s1.WeekEnd) = s2.WeekEnd;