Looking to see if there is a way to compare the number of orders entered a day and by either a specific user (EDI) or anyone else.
I can return results for per day (but only the days where a value exists) but can't figure out a way to combine all three together (Total - by EDI - by everyone else).
Any assistance greatly appreciated.
select Date, count(Order_ID)
from orders
WHERE Date >=dateadd(day,datediff(day,0,GetDate())- 7,0) and [user] = 'EDI'
and customer = '9686'
GROUP BY Date, [user];
select Date, count(Order_ID)
from orders
WHERE Date >=dateadd(day,datediff(day,0,GetDate())- 7,0) and [user] <> 'EDI'
and customer = '9686'
GROUP BY Date, [user];
select Date, count(Order_ID)
from orders
WHERE Date >=dateadd(day,datediff(day,0,GetDate())- 7,0)
and customer = '9686'
GROUP BY Date, [user];
Use conditional aggregation:
select Date, sum(case when [user] = 'EDI' then 1 else 0 end) as cnt_edi,
sum(case when [user] <> 'EDI' then 1 else 0 end) as cnt_non_edi,
count(*) as total
from orders
where Date >= dateadd(day, datediff(day, 0, GetDate()) - 7, 0) and customer = '9686'
group by Date;
Related
I'm trying to use a nested aggregate function. I know that SQL does not support it, but I really need to do something like the below query. Basically, I want to count the number of users for each day. But I want to only count the users that haven't completed an order within a 15 days window (relative to a specific day) and that have completed any order within a 30 days window (relative to a specific day). I already know that it is not possible to solve this problem using a regular subquery (it does not allow to change subquery values for each date). The "id" and the "state" attributes are related to the orders. Also, I'm using Fivetran with Snowflake.
SELECT
db.created_at::date as Date,
count(case when
(count(case when (db.state = 'finished')
and (db.created_at::date between dateadd(day,-15,Date) and dateadd(day,-1,Date)) then db.id end)
= 0) and
(count(case when (db.state = 'finished')
and (db.created_at::date between dateadd(day,-30,Date) and dateadd(day,-16,Date)) then db.id end)
> 0) then db.user end)
FROM
data_base as db
WHERE
db.created_at::date between '2020-01-01' and dateadd(day,-1,current_date)
GROUP BY Date
In other words, I want to transform the below query in a way that the "current_date" changes for each date.
WITH completed_15_days_before AS (
select
db.user as User,
count(case when db.state = 'finished' then db.id end) as Completed
from
data_base as db
where
db.created_at::date between dateadd(day,-15,current_date) and dateadd(day,-1,current_date)
group by User
),
completed_16_days_before AS (
select
db.user as User,
count(case when db.state = 'finished' then db.id end) as Completed
from
data_base as db
where
db.created_at::date between dateadd(day,-30,current_date) and dateadd(day,-16,current_date)
group by User
)
SELECT
date(db.created_at) as Date,
count(distinct case when comp_15.completadas = 0 and comp_16.completadas > 0 then comp_15.user end) as "Total Users Churn",
count(distinct case when comp_15.completadas > 0 then comp_15.user end) as "Total Users Active",
week(Date) as Week
FROM
data_base as db
left join completadas_15_days_before as comp_15 on comp_15.user = db.user
left join completadas_16_days_before as comp_16 on comp_16.user = db.user
WHERE
db.created_at::date between '2020-01-01' and dateadd(day,-1,current_date)
GROUP BY Date
Does anyone have a clue on how to solve this puzzle? Thank you very much!
The following should give you roughly what you want - difficult to test without sample data but should be a good enough starting point for you to then amend it to give you exactly what you want.
I've commented to the code to hopefully explain what each section is doing.
-- set parameter for the first date you want to generate the resultset for
set start_date = TO_DATE('2020-01-01','YYYY-MM-DD');
-- calculate the number of days between the start_date and the current date
set num_days = (Select datediff(day, $start_date , current_date()+1));
--generate a list of all the dates from the start date to the current date
-- i.e. every date that needs to appear in the resultset
WITH date_list as (
select
dateadd(
day,
'-' || row_number() over (order by null),
dateadd(day, '+1', current_date())
) as date_item
from table (generator(rowcount => ($num_days)))
)
--Create a list of all the orders that are in scope
-- i.e. 30 days before the start_date up to the current date
-- amend WHERE clause to in/exclude records as appropriate
,order_list as (
SELECT created_at, rt_id
from data_base
where created_at between dateadd(day,-30,$start_date) and current_date()
and state = 'finished'
)
SELECT dl.date_item
,COUNT (DISTINCT ol30.RT_ID) AS USER_COUNT
,COUNT (ol30.RT_ID) as ORDER_COUNT
FROM date_list dl
-- get all orders between -30 and -16 days of each date in date_list
left outer join order_list ol30 on ol30.created_at between dateadd(day,-30,dl.date_item) and dateadd(day,-16,dl.date_item)
-- exclude records that have the same RT_ID as in the ol30 dataset but have a date between 0 amd -15 of the date in date_list
WHERE NOT EXISTS (SELECT ol15.RT_ID
FROM order_list ol15
WHERE ol30.RT_ID = ol15.RT_ID
AND ol15.created_at between dateadd(day,-15,dl.date_item) and dl.date_item)
GROUP BY dl.date_item
ORDER BY dl.date_item;
I am trying to write a query that returns:
The total amount of transactions that occurred before a date range, for a particular customer.
The total amount of transactions that occurred within a date range, for a particular customer.
The total amount of payments that occurred before a date range, for a particular customer.
The total amount of payments that occurred within a date range, for a particular customer.
To that end, I've come up with the following query.
declare #StartDate DATE = '2016-08-01'
declare #EndDate DATE = '2016-08-31'
declare #BillingCategory INT = 0
select c.Id, c.Name, c.StartingBalance,
(select coalesce(sum(Amount), 0) from Transactions t where t.CustomerId = c.Id and t.[Date] < #StartDate) xDebits,
(select coalesce(sum(Amount), 0) from Transactions t where t.CustomerId = c.Id and t.[Date] >= #StartDate and t.[Data] <= #EndDate) Debits,
(select coalesce(sum(Amount), 0) from Payments p where p.CustomerId = c.Id and p.[Date] < #StartDate) xCredits,
(select coalesce(sum(Amount), 0) from Payments p where p.CustomerId = c.Id and p.[Date] >= #StartDate and p.[Date] <= #EndDate) Credits
from customers c
where c.BillingCategory in (0,1,2,3,4,5)
This query seems to give the results I want. I used the subqueries because I couldn't seem to figure out how to accomplish the same thing using JOINs. But I have a few questions.
Does this query retrieve the transaction and payment data for every single customer before filtering it according to my WHERE condition? If so, that seems like a big waste. Can that be improved?
I'd also like to add a GROUP BY to total each payment and transaction column by BillingCategory. But how can you add a GROUP BY clause here when the SELECTed columns are limited to aggregate functions if they are not in the GROUP BY clause?
The Transactions and Payments tables both have foreign keys to the Customers table.
Sample data (not real)
Customers:
Id Name BillingCategory
----- ------- ---------------
1 'ABC' 0
2 'DEF' 1
3 'GHI' 0
Transactions:
Id CustomerId Date Amount
----- ---------- ------------ ------
1 2 '2016-08-01' 124.90
2 2 '2016-08-04' 37.23
3 1 '2016-08-27' 450.02
Payments:
Id CustomerId Date Amount
----- ---------- ------------ ------
1 1 '2016-09-01' 50.00
2 1 '2016-09-23' 75.00
3 2 '2016-09-01' 100.00
You could build your sums seperately for Transactions and Payments in a CTE and then join them together:
WITH
CustomerTransactions AS
(
SELECT CustomerId,
SUM(CASE WHEN [Date] < #StartDate THEN 1 ELSE 0 END * COALESCE(Amount, 0)) AS xDebits,
SUM(CASE WHEN [Date] >= #StartDate THEN 1 ELSE 0 END * COALESCE(Amount, 0)) AS Debits
FROM Transactions
GROUP BY CustomerId
),
CustomerPayments AS,
(
SELECT CustomerId,
SUM(CASE WHEN [Date] < #StartDate THEN 1 ELSE 0 END * COALESCE(Amount, 0)) AS xCredits,
SUM(CASE WHEN [Date] >= #StartDate THEN 1 ELSE 0 END * COALESCE(Amount, 0)) AS Credits
FROM Payments
GROUP BY CustomerId
)
SELECT C.Id, c.Name, c.StartingBalance,
COALESCE(T.xDebits, 0) AS xDebits,
COALESCE(T.Debits, 0) AS Debits,
COALESCE(P.xCredits, 0) AS xCredits,
COALESCE(P.Credits, 0) AS Credits
FROM Custormers C
LEFT OUTER JOIN CustomerTransactions T ON T.CustomerId = C.Id
LEFT OUTER JOIN CustomerPayments P ON P.CustomerId = C.Id
WHERE C.BillingCategory IN(0, 1, 2, 3, 4, 5);
You can do with sub-queries to be more efficient. Pre-query grouped by each customer for only those customers who qualify by the categories in question. These sub-queries will always result in an at-most, 1 record per customer so you don't get a Cartesian result. Get that for your debits and credits and re-join back to your master list of customers with a left-join in case one side or the other (debits/credits) may not exist.
declare #StartDate DATE = '2016-08-01'
declare #EndDate DATE = '2016-08-31'
declare #BillingCategory INT = 0
select
c.ID,
c.Name,
c.StartingBalance,
coalesce( AllDebits.xDebits, 0 ) DebitsPrior,
coalesce( AllDebits.Debits, 0 ) Debits
coalesce( AllCredits.xCredits, 0 ) CreditsPrior,
coalesce( AllCredits.Credits, 0 ) Credits
from
customers c
LEFT JOIN
( select t.CustomerID,
sum( case when t.[Date] < #StartDate then Amount else 0 end ) xDebits,
sum( case when t.[Date] >= #StartDate then Amount else 0 end ) Debits
from
customers c1
JOIN Transactions t
on c1.CustomerID = t.CustomerID
where
c1.BillingCategory in (0,1,2,3,4,5)
group by
t.CustomerID ) AllDebits
on c.CustomerID = AllDebits.CustomerID
LEFT JOIN
( select p.CustomerID,
sum( case when p.[Date] < #StartDate then Amount else 0 end ) xCredits,
sum( case when p.[Date] >= #StartDate then Amount else 0 end ) Credits
from
customers c1
JOIN Payments p
on c1.CustomerID = p.CustomerID
where
c1.BillingCategory in (0,1,2,3,4,5)
group by
p.CustomerID ) AllCredits
on c.CustomerID = AllCredits.CustomerID
where
c.BillingCategory in (0,1,2,3,4,5)
COMMENT ADDITION
With respect to Thomas's answer, yes they are close. My version also adds the join to the customer table for the specific billing category and here is why. I don't know the size of your database, how many customers, how many transactions. If you are dealing with a large amount that DOES have performance impact, Thomas's version is querying EVERY customer and EVERY transaction. My version is only querying the qualified customers by the billing category criteria you limited.
Again, not knowing data size, if you are dealing with 100k records may be no noticeable performance. If you are dealing with 100k CUSTOMERS, could be a totally different story.
#JonathanWood, correct, but my version has each internal subquery inclusive of the cus
I am trying to get from my table all invoices raised for those customers who spent more than 1000 over period of last 12 months. Below is my table just for two customers as example:
And my query:
SELECT
t.Customer, t.Invoice
FROM
(SELECT
CI.Customer, CI.Invoice, CI.Date,
SUM(CASE
WHEN CI.Date > DATEADD(month, -12, getdate())
THEN CI.Valuee
ELSE 0
END) as Net
FROM
CustomerInvoice CI
GROUP BY
CI.Customer, CISRV.Invoice, CISRV.Date) AS t
GROUP BY
t.Customer, t.Invoice
HAVING
SUM (t.Net) > 1000
As result I will get only invoice INV-341453 but I would like to show also invoices INV-346218 and INV-349065.
What I am doing wrong?
Use ANSI standard window functions:
select ci.*
from (select ci.*,
sum(ci.value) over (partition by ci.customer) as total_value
from CustomerInvoice CI
where CI.Date > DATEADD(month, -12, getdate())
) ci
where total_value > 1000;
By "all invoices", I assume you mean the ones in the past twelve months.
You could do this by using a grouped query to identify the customers who have gone over the 1000 threshold, and then display all invoices for those customers:
SELECT Customer, Invoice
FROM CustomerInvoice
WHERE Customer IN
(SELECT Customer
FROM CustomerInvoice
GROUP BY Customer
HAVING SUM(CASE WHEN CI.Date>DATEADD(month,-12,getdate()) THEN CI.Valuee ELSE 0 END) > 1000)
I have this query in my ssis package and I would like to add a constraint where if the data is more than 4 days old to not add it, how would I add that into this?
SELECT PTA,
Part,
Duns,
YearStart AS BlanketYear,
VIPDoc,
DID,
STATUS = CASE
WHEN STATUS IS NULL
THEN 'Eligible'
ELSE STATUS
END,
[PRTCOMACERT STATUS] AS TMSStatus,
SupplierHTS1 AS HTS,
PrefCrit,
Producer,
NetCost,
CoCountry AS Country,
TracedValue,
Currency,
MaCountry,
VEH,
ENG,
KitPercent,
DateCreated,
DATEDIFF(d, DateCreated, GetDate()) AS AGE
FROM [SourceTempFCA].[dbo].[FCAStatus] g
INNER JOIN [SourceTempFCA].[dbo].[NEW35COMA] t ON g.part = t.PART#PART#
AND g.duns = t.[PRTCOMASUPPLIER #]
AND g.yearstart = t.[PRTCOMAEXP DATE Year]
AND g.VIPDoc = t.[PRTCOMADOC #]
--delete from [SourceTempFCA].[dbo].[FCAStatus]
This is just a SELECT statement so I'm assuming you mean to filter out those rows? Then just do
WHERE DATEDIFF(d, DateCreated, GetDate()) > 4
I have a table Supplier with two columns, TotalStock and Date. I'm trying to write a single query that will give me stock totals by week / month / year for a list of suppliers.
So results will look like this..
SUPPLIER WEEK MONTH YEAR
SupplierA 50 100 2000
SupplierB 60 150 2500
SupplierC 15 25 200
So far I've been playing around with multiple selects but I can't get any further than this:
SELECT Supplier,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-5-12'
GROUP BY Supplier
) AS StockThisWeek,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-5-1'
GROUP BY Supplier
) AS StockThisMonth,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-1-1'
GROUP BY Supplier
) AS StockThisYear
This query throws an error as each individual grouping returns multiple results. I feel that I'm close to the solution but can't work out where to go
You don't have to use subqueries to achieve what you want :
SELECT Supplier
, SUM(CASE WHEN Date >= CAST('2014-05-12' as DATE) THEN TotalStock END) AS StockThisWeek
, SUM(CASE WHEN Date >= CAST('2014-05-01' as DATE) THEN TotalStock END) AS StockThisMonth
, SUM(CASE WHEN Date >= CAST('2014-01-01' as DATE) THEN TotalStock END) AS StockThisYear
FROM StockBreakdown
GROUP BY Supplier
You may need to make the selects for the columns return only a single result. You could try this (not tested currently):
SELECT Supplier,
(
SELECT TOP 1 StockThisWeek FROM
(
SELECT Supplier, Sum(TotalStock) AS StockThisWeek
FROM StockBreakdown
WHERE Date >= '2014-5-12'
GROUP BY Supplier
) tmp1
WHERE tmp1.Supplier = Supplier
) AS StockThisWeek,
(
SELECT TOP 1 StockThisMonth FROM
(
SELECT Supplier, Sum(TotalStock) AS StockThisMonth
FROM StockBreakdown
WHERE Date >= '2014-5-1'
GROUP BY Supplier
) tmp2
WHERE tmp2.Supplier = Supplier
) AS StockThisMonth,
...
This selects the supplier and then tries to create two columns StockThisWeek and StockThisMonth by selecting the first entry from the select you created before. As through the GROUP BY there should only be one entry per supplier, so you don't lose and data.