Possible Pivot in SQL - sql

I'm trying to fetch the data from a DB to get number of Unused Applications for 60 days and 90 Days. I've tried the below:
SELECT
Uname AS [User ID],
AppName As [Application],
COUNT(*) AS [Unused App for 60 Days]
FROM APPTABLE
WHERE LAST_USE<getutcdate()-60
GROUP BY Uname, AppNameNow
I want to do the same for 90 days as well but in this same query. I'm having difficulty in doing it.
Could you help me understand how to do it? Appreciate your response.

Use conditional aggregation. I would suggest using dateadd() to be explicit about the intention of the logic:
SELECT Uname AS [User ID], AppName As [Application],
SUM(CASE WHEN LAST_USE < DATEADD(DAY, -60, getutcdate()) THEN 1 ELSE 0 END) as unused_60,
SUM(CASE WHEN LAST_USE < DATEADD(DAY, -90, getutcdate()) THEN 1 ELSE 0 END) as unused_90
FROM APPTABLE
WHERE LAST_USE < DATEADD(DAY, -60, getutcdate())
GROUP BY Uname, AppNameNow
Note that the WHERE clause is still filtering the dates. But you need to be careful that the WHERE is consistent with the time frames in the SELECT.

count(*) is logically equivalent to sum(1).
Instead, use a conditional case:
sum(case when LAST_USE<getutcdate()-60 then 1 else 0 end) as [Unused App for 60 Days]
,sum(case when LAST_USE<getutcdate()-90 then 1 else 0 end) as [Unused App for 90 Days]
of course, include the larger set in your where

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

SQL case operation

Im fairly new with sql, and been trying to solve a problem where you have a table information about orders. In this case, Im trying to use the case operation to get a monthly report on orders, so I should have a column which states the year,another one which states the month, and then I should have columns for days 1-20,21-22,23-24 and above 25. Im trying to use the case operation to get the amount of orders that happened on those days.
I tried the following query :
SELECT
DATEPART(YEAR,date) AS year,DATEPART(MONTH,date) AS month,
COUNT(CASE WHEN DATEPART(DAY,date) BETWEEN 1 AND 20 THEN order ELSE 0 END) AS D1_D20,
COUNT(CASE WHEN DATEPART(DAY,date) BETWEEN 21 AND 22 THEN order ELSE 0 END) AS D21_D22,
COUNT(CASE WHEN DATEPART(DAY,date) BETWEEN 23 AND 24 THEN order ELSE 0 END) AS D23_D24,
COUNT(CASE WHEN DATEPART(DAY,date) > 25 THEN order ELSE 0 END) AS D25_END
FROM ORDERS
GROUP BY DATEPART(YEAR,date),DATEPART(MONTH,date)
Obviously the problem with that query is that, now I just get the total number of orders for each of the days, I know I should count the orders, but dont know the syntax. Help would be greatly appreciated!
Use SUM():
SELECT
DATEPART(YEAR, date) AS year, DATEPART(MONTH, date) AS month,
SUM(CASE WHEN DATEPART(DAY,date) BETWEEN 1 AND 20 THEN 1 ELSE 0 END) AS D1_D20,
SUM(CASE WHEN DATEPART(DAY,date) BETWEEN 21 AND 22 THEN 1 ELSE 0 END) AS D21_D22,
SUM(CASE WHEN DATEPART(DAY,date) BETWEEN 23 AND 24 THEN 1 ELSE 0 END) AS D23_D24,
SUM(CASE WHEN DATEPART(DAY,date) > 25 THEN 1 ELSE 0 END) AS D25_END
FROM ORDERS
GROUP BY DATEPART(YEAR, date), DATEPART(MONTH, date);
I would recommend using the functions DAY(), YEAR(), and MONTH() because they are simpler to type.
By the way, you can use COUNT() if you remove the ELSE clause. Your particular problem is that COUNT(0) = COUNT(1) because COUNT() counts non-NULL values. I prefer SUM() because it is more intuitive in this respect.

How to handle aggregate function in Case statements when it involves date columns

I'm calculating invoiceDate vs currentDate then sum Value/Amount column grouping by Customer but its returning "invoiceDate is not contained in either an aggregate function or the GROUP BY clause"
select Customer
,case
when datediff(dd,InvoiceDate,getdate()) <=30 then sum(InvoiceBal1)
else 0
end as [Current]
,case
when datediff(dd,InvoiceDate,getdate()) between 31 and 60 then sum(InvoiceBal1)
else 0
end as [30 Days]
from CusInvoice
group by Customer
You want conditional aggregation:
select Customer
sum(case when datediff(day, InvoiceDate, getdate()) <= 30
then InvoiceBal1 else 0
end) as balance_current
sum(case when datediff(day, InvoiceDate, getdate()) between 31 and 60
then InvoiceBal1 else 0
end) as balance_30days
from CusInvoice
group by Customer

Sql query to find and sum orders placed in the morning throws error

I am working on a sql query, in sql server 2012, to find out how many orders were placed in the morning. The table name is Order_Data which has a column, Order_TimeStamp with the value when the order was placed. The following is my query:
select sum(case when
substring(CONVERT(varchar,O.OrderTimestamp,108),1,2)>='05'
and substring(CONVERT(varchar,O.OrderTimestamp,108),1,2)<='11'
then 1 else 0) as orderCountTimeMorning
from Order_Data O where
o.email_address = 'abc#yahoo.com'
When I run this query I am getting "Incorrect syntax near ')'" error. This happens at the "then 1 else 0)" part of the query. I tried to match the brackets and they seem to match fine.
Use datepart():
select sum(case when datepart(hour, ordertimestamp) between 5 and 11
then 1 else 0
end) as orderCountTimeMorning
from Order_Data O
where o.email_address = 'abc#yahoo.com';
Or, convert the value to a time:
select sum(case when convert(time, ordertimestamp) >= '05:00:00' and
convert(time, ordertimestamp) < '12:00:00'
then 1 else 0
end) as orderCountTimeMorning
from Order_Data O
where o.email_address = 'abc#yahoo.com';
There is no need to convert date/time values to strings for such comparisons.
It sound like what you really want is;
Select count(*) from Order_Data
where convert(time, ordertimestamp) >= '05:00:00'
and convert(time, ordertimestamp) < '12:00:00'
and email_address = 'abc#yahoo.com'

T-SQL Get 3 Month Interval : Trying to count Property_IDs over a close_dt Interval

Trying to count Property_IDs over a close_dt Interval
select distinct pd.state AS StateName, zw.CountyName AS [County Name]
,sum(case when pc.close_dt >= dateName(MM,dateadd(MM,-3,GetDate()))then 1
else 0 end) AS [0-3 Months]
,sum(case when pc.close_dt >= dateName(MM,dateadd(MM,-6,GetDate()))
and pc.close_dt < dateName(MM,dateadd(MM,-3,GetDate())) then
1 else 0 end) AS [3-6 Months]
from resnet_mysql.dbo.property_details pd (Nolock)
join resnet.dbo.ZipCodesView zw (nolock)
on CAST(LEFT(pd.zip, 5) AS varchar) = CAST(zw.ZipCodeID AS varchar)
join resnet_mysql.dbo.property_closings pc (nolock)
on pd.property_id = pc.property_id
group by pd.state, zw.countyName, pc.close_dt
How can I get the 3 month interval from the previous 3 month interval value? So it will be 3-6 months?
I want it to look like this.
But I get this error.
I am thinking you want something like this:
select pd.state AS StateName, zw.CountyName AS [County Name],
sum(case when pc.close_dt >= dateadd(month, -3, GetDate()) then 1
else 0
end) AS [0-3 Months]
sum(case when pc.close_dt >= dateadd(month, -6, GetDate()) and
pc.close_dt < dateadd(month, -3, GetDate())
then 1
else 0
end) AS [3-6 Months]
from resnet_mysql.dbo.property_details pd join
resnet.dbo.ZipCodesView zw
on LEFT(pd.zip, 5) = CAST(zw.ZipCodeID as VARCHAR(5)) join
resnet_mysql.dbo.property_closings pc
on pd.property_id = pc.property_id
group by pd.state, zw.countyName;
Your original code has so many errors, it is hard to list them:
DATENAME() returns a string. Why would you want to compare that to a date?
You are aggregating based on date ranges. You don't want to include the date in the GROUP BY.
LEFT() already returns a string; there is no need to convert it.
You probably don't want to compare a string version of zip code to a numeric id. But if you do, the conversion should specify the length.
WITH (NOLOCK) is not recommended unless you actually know what you are doing.