Count by day, count by week, in a grouped select statement - sql

I am trying to count instances of a status by current day and current week, grouped by town.
(The table has just 3 columns: Town, status, status_date)
SELECT
MAX(dbo.Clients.Town) AS Town,
CASE
WHEN MAX(datepart(wk, status_date)) = DATEPART(wk, getdate()) THEN COUNT(Town)
ELSE 0
END AS wkTotal,
CASE
WHEN MAX(CONVERT(date, status_date, 106)) = CONVERT(date, getdate(), 106) THEN COUNT(Town)
ELSE 0
END AS dayTotal
FROM
dbo.Clients
WHERE
dbo.Clients.Status LIKE 'Status 1%'
AND MONTH(GETDATE()) = MONTH(dbo.Clients.Status_date)
AND YEAR(GETDATE())= YEAR(dbo.Clients.Status_date)
GROUP BY
dbo.Clients.Town
ORDER BY
dbo.Clients.Town
This code just returns a month count for both day total and week total columns
Hope you can help.

I surgest you do this:
SELECT dbo.Clients.Town AS Town,
count(*) AS wkTotal,
sum(CASE WHEN datepart(dayofyear, status_date) = DATEPART(dayofyear, getdate()) THEN 1 ELSE 0 END) AS dayTotal
FROM dbo.Clients
WHERE dbo.Clients.Status LIKE 'Status 1%' AND
datepart(week, GETDATE()) = datepart(week, dbo.Clients.Status_date)
AND YEAR(GETDATE())= YEAR(dbo.Clients.Status_date)
GROUP BY dbo.Clients.Town
ORDER BY dbo.Clients.Town

Related

Return 1 row with various sums based on date?

Assume a table of purchase transactions with columns CustId, Amount, DatePosted where Amount is the value of the transaction, and DatePosted is a DATETIME value. Given a specific CustId, how would I write a select such that it returns a single row with the following columns: CustId, total value of transactions in the last 3 days, last 60 days, 1 year, 2 years (5 columns total).
Example table:
CustId
Amount
DatePosted
1234
698.02
2023-01-23Z12:34:56
1234
582.69
2022-12-15Z19:57:23
1234
7775.22
2022-12-02Z02:34:32
1234
18.72
2022-01-23Z12:34:56
1234
2.27
2021-01-23Z12:34:56
Expected output given the sample data above when searching using CustId=1234:
CustId
3-day Total
60-day Total
1 year Total
2 year Total
1234
698.02
9055.93
9074.65
9076.92
You could get all purchase data for the last 2 years, then using SUM with SQL CASE expression to calculate total value for each time-range.
SELECT
CustId,
SUM(CASE WHEN DatePosted >= Last3Day THEN Amount ELSE 0 END) AS [3-day Total],
SUM(CASE WHEN DatePosted >= Last60Day THEN Amount ELSE 0 END) AS [60-day Total],
SUM(CASE WHEN DatePosted >= Last1Year THEN Amount ELSE 0 END) AS [1 year Total],
SUM(CASE WHEN DatePosted >= Last2Year THEN Amount ELSE 0 END) AS [2 year Total]
FROM
<your data table>,
(SELECT
DATEADD(DAY, -3, GETDATE()) AS Last3Day,
DATEADD(DAY, -60, GETDATE()) AS Last60Day,
DATEADD(YEAR, -1, GETDATE()) AS Last1Year,
DATEADD(YEAR, -2, GETDATE()) AS Last2Year) timerange
WHERE DatePosted >= Last2Year
GROUP BY CustId;
Demo: http://sqlfiddle.com/#!18/9eecb/179880
This query assumes 2 year max. If you want to go further back then change the where clause as well. No need to use coalesce or a derived table. SQL server query planner may be smart enough to provide similar performance for all these solutions but this is easier to understand:
SELECT
CustId,
SUM(CASE WHEN DatePosted >= DATEADD(day, -3, GETDATE()) THEN Amount ELSE 0 END) AS [3-day Total],
SUM(CASE WHEN DatePosted >= DATEADD(day, -60, GETDATE()) THEN Amount ELSE 0 END) AS [60-day Total],
SUM(CASE WHEN DatePosted >= DATEADD(year, -1, GETDATE()) THEN Amount ELSE 0 END) AS [1 year Total],
SUM(Amount) AS [2 year Total]
FROM PurchaseTransactions
WHERE CustId = 1234 AND DatePosted >= DATEADD(year, -2, GETDATE())
GROUP BY CustId
This is set up so that you can set #CustID = null and the query will return results for all customers in the set.
EDIT: Updated my query below to give you more flexibility across your desired ranges should you wish to derive additional heuristics. (Counts, averages, etc.)
Also removed coalesce as it's simply not needed here.
DECLARE #CustID BIGINT;
SELECT table1.custID,
SUM([3Day].amount) AS [3DayTotal],
COUNT([3Day].amount) AS [3DayCount]
SUM([60Day].amount) AS [60DayTotal],
SUM([1Year].amount) AS [1YearTotal],
Sum([2Year].amount) AS [2YearTotal],
AVG([2Year].amount) AS [2YearAverage]
FROM table1 LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(DAY, -3, GETDATE())) AS [3Day] ON table1.CustID = [3Day].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(DAY, -60, GETDATE())) AS [60Day] ON table1.CustID = [60Day].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(YEAR, -1, GETDATE())) AS [1Year] ON table1.CustID = [1Year].CustID LEFT OUTER JOIN
(SELECT custID, Amount FROM table1 WHERE DatePosted > DATEADD(YEAR, -2, GETDATE()) AS [2Year] ON table1.CustID = [2Year].CustID
WHERE table1.CustID = #CustID
OR #CustID IS NULL
GROUP BY table1.CustID

Two SQL Server queries with different where clause

I'm trying to merge two very similar queries. The only different thing is in the WHERE clause:
SELECT
CONVERT(VARCHAR(10), Date, 101) as Date,
COUNT(*) as WinnerCount
FROM
table t1
WHERE
t1.Result = 'Winner'
GROUP BY
CONVERT(VARCHAR(10), Date, 101)
and the second with Loser clause:
SELECT
CONVERT(VARCHAR(10), Date, 101) as Date,
COUNT(*) as LoserCount
FROM
table t1
WHERE
t1.Result = 'Loser'
GROUP BY
CONVERT(VARCHAR(10), Date, 101)
What I'm trying to achieve is to get a result set like this:
Date | WinnerCount | LoserCount
01/01/2017 24 16
17/02/2017 13 9
I've tried to merge this with join as two resultset:
SELECT
FirstSet.Date
FROM
(SELECT
CONVERT(VARCHAR(10), Date, 101) as Date,
COUNT(*) as Count
FROM
table t1
WHERE
t1.Result = 'Winner'
GROUP BY
CONVERT(VARCHAR(10), Date, 101)) AS FirstSet
JOIN
(SELECT
CONVERT(VARCHAR(10), Date, 101) as Date,
COUNT(*) as Count
FROM
table t1
WHERE
t1.Result = 'Loser'
GROUP BY
CONVERT(VARCHAR(10), Date, 101)) AS SecondSet ON FirstSet.Date = SecondSet.Date
ORDER BY
FirstSet.Date
but in results I got only dates :/
You may use this query. Insted of counting all the rows, you may SUM all the occurrences of the required text (*Winner" or "Loser" in this case).
SELECT
CONVERT(VARCHAR(10), Date, 101) as Date,
SUM(CASE WHEN t1.Result = 'Winner' THEN 1 ELSE 0 END) as WinnerCount,
SUM(CASE WHEN t1.Result = 'Loser' THEN 1 ELSE 0 END) as LoserCount
from table t1
where t1.Result in ( 'Winner', 'Loser')
group by
CONVERT(VARCHAR(10), Date, 101)
The above accepted answer will provide you with the correct result however if you wanted to keep the Count and not use Sum to aggregate the results then you could alternatively use the below:
select
CONVERT(VARCHAR(10), Date, 101) as Date,
COUNT(CASE WHEN t1.Result = 'Winner' THEN 1 ELSE NULL END) as WinnerCount,
COUNT(CASE WHEN t1.Result = 'Loser' THEN 1 ELSE NULL END) as LoserCount
from table as t1
where t1.Result in ('Winner', 'Loser')
group by
CONVERT(VARCHAR(10), Date, 101)
You can try something like:
SELECT
CONVERT(VARCHAR(10), Date, 101) as Date,
SUM(CASE WHEN t1.Result = 'Winner' THEN 1 ELSE 0) AS WinnerCount,
SUM(CASE WHEN t1.Result = 'Loser' THEN 1 ELSE 0) AS LoserCount
FROM
table t1
group by
CONVERT(VARCHAR(10), Date, 101)

SQL - Average on DateDiff with taking out weekends

I have an SQL statement that calculates how many days late our vendors pay for invoices. It only includes weekdays and it works perfectly. My customer is now asking if I can have an average of those days late per vendor. Here is my code that works:
DATEDIFF(dd, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE))
-(DATEDIFF(wk, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) * 2)
-(CASE WHEN DATENAME(dw, PURCHTABLE.CONFIRMEDDLV) = 'Sunday'
THEN 1
ELSE 0
END)
-(CASE WHEN DATENAME(dw, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) = 'Saturday'
THEN 1
ELSE 0
END) AS 'DAYS LATE'
If I try to put this in an AVG(), but it tells me I cannot perform an aggregate function on an aggregate function.
UPDATE:
This is my original SQL:
SELECT
PURCHTABLE.PURCHNAME AS 'VENDOR NAME',
PURCHTABLE.ORDERACCOUNT AS 'VENDOR NUMBER',
COUNT(DISTINCT PURCHTABLE.PURCHID) AS 'PURCHASE ORDER',
COUNT(PURCHLINE.LINENUMBER) AS 'NUMBER OF LINES',
SUM(PURCHLINE.LINEAMOUNT) AS 'PO PRICE TOTAL',
DATEDIFF(dd, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE))-(DATEDIFF(wk, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) * 2)-(CASE WHEN DATENAME(dw, PURCHTABLE.CONFIRMEDDLV) = 'Sunday' THEN 1 ELSE 0 END)-(CASE WHEN DATENAME(dw, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) = 'Saturday' THEN 1 ELSE 0 END) AS 'DAYS LATE'
FROM
PURCHTABLE
JOIN
PURCHLINE ON PURCHLINE.PURCHID = PURCHTABLE.PURCHID
JOIN
VENDPACKINGSLIPJOUR ON VENDPACKINGSLIPJOUR.PURCHID = PURCHTABLE.PURCHID
WHERE
PURCHTABLE.DELIVERYDATE >= '2017-01-01'
AND
PURCHTABLE.DELIVERYDATE <= '2017-01-20'
AND
PURCHTABLE.ORDERACCOUNT = 'VN03526'
GROUP BY
PURCHTABLE.PURCHNAME,
PURCHTABLE.ORDERACCOUNT,
PURCHTABLE.DELIVERYDATE,
PURCHTABLE.CONFIRMEDDLV
So I don't quite understand how I change that to what you are saying.....I'm tried this but it's not working.
SELECT
PURCHTABLE.PURCHNAME AS 'VENDOR NAME',
PURCHTABLE.ORDERACCOUNT AS 'VENDOR NUMBER'
FROM
(SELECT
PURCHTABLE.PURCHNAME AS 'VENDOR NAME',
PURCHTABLE.ORDERACCOUNT AS 'VENDOR NUMBER',
COUNT(DISTINCT PURCHTABLE.PURCHID) AS 'PURCHASE ORDER',
COUNT(PURCHLINE.LINENUMBER) AS 'NUMBER OF LINES',
SUM(PURCHLINE.LINEAMOUNT) AS 'PO PRICE TOTAL',
DATEDIFF(dd, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE))-(DATEDIFF(wk, PURCHTABLE.CONFIRMEDDLV, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) * 2)-(CASE WHEN DATENAME(dw, PURCHTABLE.CONFIRMEDDLV) = 'Sunday' THEN 1 ELSE 0 END)-(CASE WHEN DATENAME(dw, MAX(VENDPACKINGSLIPJOUR.DELIVERYDATE)) = 'Saturday' THEN 1 ELSE 0 END) AS 'DAYS LATE'
FROM
PURCHTABLE
JOIN
PURCHLINE ON PURCHLINE.PURCHID = PURCHTABLE.PURCHID
JOIN
VENDPACKINGSLIPJOUR ON VENDPACKINGSLIPJOUR.PURCHID = PURCHTABLE.PURCHID
WHERE
PURCHTABLE.DELIVERYDATE >= '2017-01-01'
AND
PURCHTABLE.DELIVERYDATE <= '2017-01-20'
AND
PURCHTABLE.ORDERACCOUNT = 'VN03526') A
GROUP BY
PURCHTABLE.PURCHNAME,
PURCHTABLE.ORDERACCOUNT,
PURCHTABLE.DELIVERYDATE,
PURCHTABLE.CONFIRMEDDLV
The code you have at the moment will be part of a larger query, such as:
select VendorID
,VendorName
...
...
,<Your Days Late Code> as DaysLate
,InvoiceAmount
...
from tables
group by VendorID
,VendorName
all you need to do to average over all of this is pick the other columns you want to do the average by and wrap the whole thing in another select statement:
select VendorID
,VendorName
,avg(DaysLate) as AverageDaysLate
,sum(InvoiceAmount) as TotalInvoiceAmount
from(
select VendorID
,VendorName
...
...
,<Your Days Late Code> as DaysLate
,InvoiceAmount
...
from tables
group by VendorID
,VendorName
) a
group by VendorID
,VendorName

SQL: Create Multiple Selects Based One Date

I've been searching for help on this but I can't seem to find the solution. What I have is:
Table called Details_Orders.
Columns: CustomerName, InvoiceDate, SaleAmount and SaleCost.
What I want to do is select a date ('1-1-2015') and display the Customer Name, the Date ('1-1-2015'), the sum of SaleAmount for that Date, and the Sum of the SaleCost. And here's the part I'm having problems with, I want the next columns to display the previous months SaleAmount and SaleCost, then the previous YEARS SaleAmount and SaleCost.
I'm having trouble figuring out how to code the Previous Time Periods Select statements. Any help on this would be greatly appreciated.
Select
CustomerName,
InvoiceDate,
Sum(SaleAmount),
Sum(SaleCost),
*PREVIOUS MONTH Sum(SalesAmount) DATEADD(MONTH,-1,'1-1'2015'),
PREVIOUS MONTH Sum(SalesCost) DATEADD(MONTH,-1,'1-1'2015'),
PREVIOUS YEAR Sum(SalesAmount) DATEADD(YEAR,-1,'1-1'2015'),
PREVIOUS YEAR Sum(SalesCost) DATEADD(YEAR,-1,'1-1'2015')*
From Details_Orders
WHERE InvoiceDate='1-1-2015'
Basic
You want to use conditional aggregation, and also need a group by. It is a bit unclear what the exact definitions of previous month and year are. Here is one interpretation:
Select CustomerName,
Sum(case when InvoiceDate = thedate then SaleAmount else 0 end),
Sum(case when InvoiceDate = thedate then SaleCost else 0 end),
Sum(case when year(InvoiceDate)*12 + month(InvoiceDate) =
year(thedate)*12 + month(thedate) - 1
then SaleAmount else 0
end),
Sum(case when year(InvoiceDate)*12 + month(InvoiceDate) =
year(thedate)*12 + month(thedate) - 1
then SaleCost else 0
end),
Sum(case when InvoiceDate < thedate and
InvoiceDate >= dateadd(year, -1, thedate
then SaleAmount else 0
end),
Sum(case when InvoiceDate < thedate and
InvoiceDate >= dateadd(year, -1, thedate
then SaleCost else 0
end)
From (select cast('2015-01-01' as date) as thedate
) params cross join
Details_Orders o
group by CustomerName;

Duplicates from an SQL Query

I have a dataset I retrieve from multiple joins. I have used SELECT DISTINCT in my statements but I still see duplicates in the result set. Here is the code:
SELECT DISTINCT Account
, PayoffAmtDOL as 'Payoff Amount DOL'
, PayoffAmtLOG as 'Payoff Amount LOG'
, PayoffAmountLive as 'Payoff Amount Live'
, [Difference]
, PrincipalBalance as 'Principal Balance'
, CreationDate as 'Date Entered System'
, CACSState as 'CACS State at Entry'
, PaymentsMade AS 'Payments Made'
, TotalPaymentAmount as 'Total Payment Amount'
, 'Liquidation Percentage' = CASE WHEN PayoffAmountLive = 0 THEN 1
WHEN ISNULL([Difference],0) = ISNULL(PayoffAmtDOL, 0) THEN 1
WHEN ISNULL([Difference],0) < 0 AND ISNULL(PayoffAmtDOL, 0) > 0 THEN 0
WHEN ISNULL([Difference],0) > 0 AND ISNULL(PayoffAmtDOL, 0) < 0 THEN 1
WHEN ISNULL([Difference],0) > ISNULL(PayoffAmtDOL, 0) THEN 1
WHEN [Difference] > 0 AND ISNULL(PayoffAmtDOL, 0) = 0 THEN 1
WHEN ISNULL(PayoffAmtDOL, 0) = 0 THEN 0
ELSE ISNULL([Difference],0)/ISNULL(PayoffAmtDOL, 0) END
, Cnt = 1
FROM
(
SELECT DISTINCT a.Account,
c.PayoffAmtDOL,
c.PayoffAmtLOG,
(ISNULL(c.PayoffAmtCACS, cacs.payoff_amt)) as 'PayoffAmountLive',
(ISNULL(c.PayoffAmtDOL, 0) - (ISNULL(c.PayoffAmtCACS , ISNULL(cacs.payoff_amt, 0)))) as 'Difference',
c.PrincipalBalance,
c.CreationDate,
c.CACSState,
(SELECT COUNT(PaymentID)
FROM tblATLPaymentInfo p
WHERE p.AccountID = a.AccountID
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) <= '3/27/2014'
) as 'PaymentsMade',
(SELECT SUM(PaymentAmount)
FROM tblATLPaymentInfo p
WHERE p.AccountID = a.AccountID
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) <= '3/27/2014'
) as 'TotalPaymentAmount'
FROM tblATLAcctInfo a
RIGHT JOIN tblATLClaimInfo c
ON c.AccountID = a.AccountID
LEFT JOIN SCFLOKYDCMSQL03.CACS_DM.dbo.Cacs_Info cacs
ON cacs.Account = a.Account
WHERE CONVERT(DATETIME, CONVERT(VARCHAR(10), c.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), c.CreationDate, 101)) <= '3/27/2014'
AND c.ClaimTypeID = (SELECT DISTINCT ClaimTypeID FROM tblATLClaimType WHERE ClaimType = 'N02 - Claims')
) a
ORDER BY Account
Here is an example of the duplicate rows:
AccountID DateEntered
123 01/19/2014
123 01/21/2014
345 02/1/2014
345 02/10/2014
The difference between appears to be the date entered. Maybe selecting the Row_Number() and then deleting the later date could be a solution
DISTINCT should not return multiple rows.. there should be at least one column that is different in each row, no? With character data, sometimes one can be fooled by non-visible differences, such as trailing spaces. Not sure if that is the case here, though.
Can you give an example of the duplicate rows?
OK, I see your edit. You have to select which of the dates to display. Try this to get the earliest date per AccountID:
SELECT AccountID, MIN(DateEntered) AS DateEntered
FROM ....
GROUP BY AccountID
ORDER BY AccountID
You can add more columns in the SELECT, as long as they are distinct you will not get more rows.
If you want, you can add COUNT(*) to the select to get the number of rows grouped.
DISTINCT will only reject lines that are exact duplicates, the DateEntered is different on each ID. If you want the latest, use Max(DateEntered)