Customer Attrition in SQL Server - sql-server

I am trying to find churned customer in the adventureworks2012 sample database. Churned customer in my case is a customer who used to be active in a period of six months, then has not made any transaction in a later period of time.
My attributes are customer ID, product category, product subcategories, price, order dates, online order flag etc
Can you please provide some hints on how to define teacher customer as either churned or not churned.
Thank you in advance.

If you're trying to loop through them all:
SELECT TEACHER_ID, MAX(ORDER_DATE) FROM ORDERS GROUP BY TEACHER_ID
Will give you each teachers last order date, and then you can loop through and compare them to whatever date range you want (if you're doing a loop).
If you're trying to figure out an individual teacher just see if the count is > 0:
SELECT COUNT(*) AS COUNT FROM ORDERS WHERE TEACHER_ID = ? AND ORDER_DATE > (NOW() - INTERVAL 6 MONTH)
Best I can do w\o a better idea what language you're using and what the application is.

Related

MSSQL, how compare records with dataframe table?

let's say we have a table with invoices and employees ID
and another table for vacation which also contains an employee ID.
In this case only emplyee1 had vacation a these date frames.
Now we have to compare if the invoice date is between or exactly at the date frames of vacation table.
The aim is: The invoice date has to be compared, if an employee is on vacation the calc_flag = 0, if not then always=1. E.g. employee1 was from 2023/01/05 till 2023/01/12 on vacation.
So all his invoices must be calc_flag=0 for this time.
How to create the sql query in mssql for this topic?
Thanks for any advice.
I already tried to check the dateframes but if there are several entries in vacation table, I'm not sure how to handle it.
One way to do this is a LEFT JOIN to check for existance. Perhaps you need to tweak the < or > depening if you would like to include the Begin and or End date.
SELECT i.InvNo,
i.emp_ID,
i.InvDate,
i.calc_flag ,
CASE WHEN v.Emp_ID IS NOT NULL THEN 0 ELSE 1 END AS calc_flage
FROM dbo.invoices AS i
LEFT JOIN dbo.vacation AS v ON v.Emp_ID = i.emp_ID AND v.[Begin] < i.InvDate AND v.[End] > i.InvDate

How do you efficiently pull data from multiple records into 1 record

I currently have data in a table related to transactions. Each record has a purchase ID, a Transaction Number, and up to 5 purchases assigned to the transaction. Associated with each purchase there can be up to 10 transactions. For the first transaction of each purchase I need a field that is a string of each unique purchase concatenated. My solution was slow I estimated it would take 40 days to complete. What would be the most effective way to do this?
What you are looking for can be achieved in 2 steps:
Step1: Extracting the first transaction of each purchase
Depending upon your table configuration this can be done in a couple of different ways.
If your transaction IDs are sequential, you can use something like:
select * from table a
inner join
(select purchaseid,min(transactionid) as transactionid
from table group by purchaseid) b
on a.purchaseid-b.purchaseid and a.transactionid=b.transactionid
If there is a date variable driving the transaction sequence, then:
select a.* from
(select *,row_number() over(partition by purchaseid order by date) as rownum from table)a
where a.rownum=1
Step2: Concatenating the Purchase details
This can be done by using the String_agg function if you are using the latest version of SQL server. If not, the following link highlights a couple of different ways you can do this:
Optimal way to concatenate/aggregate strings
Hope this helps.

SQL Server find column difference from different tables and fill intermediate dates

I have the purchase entry in one table #temp1 and sales history in another table #temp2 for multiple stores. There might be no sales, no purchase, or both/either of them in a day. I need to build a graph of daily stock.
Basically, I am stuck in the query part. For first part I need to combine both tables to view the data together...
Secondly, I need to find the cumulative values for the stock ; something like ...
After I get I need to plot it finally... help out !!! QUERY MASTER !!!
If you start out by using a Union something like:
SELECT Store, Date, Purchase, 0 Sales FROM #temp1
UNION ALL
SELECT Store, Date, 0, Sales FROM #temp2
You have all the data in one table/view. From there, you can get things consolidated by
SELECT
Store, Date,
Sum(Purchase) Purchase,
Sum(Sales) Sales,
Sum(Purchase) - Sum(Sales) InStock
GROUP BY
Store, Date
That will give you a view with the Store, Date, Purchases, Sales and In Stock in one row. If you work things via query rather than temp tables, you can easily use the final view to feed SSRS and draw your graph.
Hope that helps.
Yes, the hint by #mark worked...
select store,date,ISNULL(purchase,0) as purchase,0 as sales
into #tbl
from temp1
union all
select store,date,0 as purchase,ISNULL(sales,0) as sales from temp2
select store,sum(purchase) as PUR,sum(sales) as SAL,sum(purchase-sales) as STOCK from #tbl
group by store,date
order by storename
drop table #tbl
And, the empty dates in between are automatically managed by the SSRS reporting tool.
But, the cumulative sums are not able to solve till now...

CakePHP - How to use calculations from associated data in query

I'm trying to figure out the best way to do something - basically I'm looking for advice before I do it the long/hard way!
I have the following model associations:
Seller hasMany Invoices
Invoice hasOne Supplier
Supplier belongsTo SupplierType
Each invoice is for a certain amount and is from a certain date. I want to be able to retrieve Sellers who have spent within a certain amount in the past 'full' month for which we have data. So, I need to get the date 1 month before the most recent invoice, find the total on all invoices for that Seller since that date, and then retrieve only those where the total lies between, say, £10000 and £27000 (or whatever range the user has set).
Secondly, I want to be able to do the same thing, but with the SupplierType included. So, the user may say that they want Sellers who have spent between £1000 & £5000 from Equipment Suppliers, and between £1000 & £7000 from Meat Suppliers.
My plan here is to do an inital search for the appropriate supplier type id, and then I can filter the invoices based on whether each one is from a supplier of an appropriate type.
I'm mainly not sure whether there is a way to work out the monthly total and then filter on it in one step. Or am I going to have to do it in several steps? I looked at Virtual Fields, but I don't think they do what I need - they seem to be mainly used to combine fields from the same record - is that correct?
(Posted on behalf of the question author).
I'm posting the eventual solution here in case it helps anyone else:
SELECT seller_id FROM
(SELECT i.seller_id, SUM(price_paid) AS totalamount FROM invoices i
JOIN
(SELECT seller_id, MAX(invoice_date) AS maxdate FROM invoices) sm
ON i.seller_id = sm.seller_id
WHERE i.invoice_date > (sm.maxdate - 30) GROUP BY seller_id) t
WHERE t.totalamount BETWEEN 0 AND 1000
This can be done in a single query that will look something like:
select * from (
select seller, sum(amount) as totalamount
from invoices i join
(select seller, max(invoicedate) as maxdate from invoices group by seller) sm
on i.seller=sm.seller
and i.invoicedate>(sm.maxdate-30)
group by seller
) t where t.totalamount between 1000 and 50000

Filtering a complex SQL Query

Unit - hmy, scode, hProperty
InsurancePolicy - hmy, hUnit, dtEffective, sStatus
Select MAX(i2.dtEffective) as maxdate, u.hMy, MAX(i2.hmy) as InsuranceId,
i2.sStatus
from unit u
left join InsurancePolicy i2 on i2.hUnit = u.hMy
and i2.sStatus in ('Active', 'Cancelled', 'Expired')
where u.hProperty = 2
Group By u.hmy, i2.sStatus
order by u.hmy
This query will return values for the Insurance Policy with the latest Effective Date (Max(dtEffective)). I added Max(i2.hmy) so if there was more than one Insurance Policy for the latest Effective Date, it will return the one with the highest ID (i2.hmy) in the database.
Suppose there was a Unit that had 3 Insurance Policies attached with the same latest effective date and all have different sStatus'.
The result would look like this:
maxdate UnitID InsuranceID sStatus
1/23/12 2949 1938 'Active'
1/23/12 2949 2343 'Cancelled'
1/23/12 2949 4323 'Expired'
How do I filter the results so that if there are multiple Insurance Policies with different Status' for the same unit and same date, then we choose the Insurance Policy with the 'Active' Status first, if one doesn't exist, choose 'Cancelled', and if that doesn't exist, choose 'Expired'.
This seems to be a matter of proper ranking of InsurancePolicy's rows and then joining Unit to the set of the former's top-ranked rows:
;
WITH ranked AS (
SELECT
*,
rnk = ROW_NUMBER() OVER (
PARTITION BY hUnit
ORDER BY dtEffective DESC, sStatus, hmy DESC
)
FROM InsurancePolicy
)
SELECT
i2.dtEffective AS maxdate,
u.hMy,
i2.hmy AS InsuranceId,
i2.sStatus
FROM Unit u
LEFT JOIN ranked i2 ON i2.hUnit = u.hMy AND i2.rnk = 1
You could make this work with one SQL statement but it will be nearly unreadable to your everyday t-sql developer. I would suggest breaking this query up into a few steps.
First, I would declare a table variable and place all the records that require no manipulation into this table (ie - Units that do not have multiple statuses for the same date = good records).
Then, get a list of your records that need work done on them (multiple statuses on the same date for the same UnitID) and place them in a table variable. I would create a "rank" column within this table variable using a case statement as illustrated here:
Pseudocode: WHEN Active THEN 1 ELSE WHEN Cancelled THEN 2 ELSE WHEN Expired THEN 3 END
Then delete records where 2 and 3 exist with a 1
Then delete records where 2 exists and 3
Finally, merge this updated table variable with your table variable containing your "good" records.
It is easy to get sucked into trying to do too much within one SQL statement. Break up the tasks to make it easier for you to develop and more manageable in the future. If you have to edit this SQL in a few years time you will be thanking yourself, not to mention any other developers that may have to take over your code.

Resources