Build customers report of last years with T-SQL - sql

I've 3 tables (simplified):
-----------Orders--------------------
Id | Total_Price | Customer_Id | Date
--------Order Details---------------------
Id | Order_Id | Product Name | Qty | Value
----Customers------
Id | Name | Address
I take a total order value of single customer with this query:
SELECT C.ID, C.NAME , SUM(O.TOTAL_PRICE)
FROM CUSTOMERS C
JOIN ORDERS O ON O.CUSTOMER_ID = C.ID
GROUP BY C.ID, C.NAME
Now, I want to build a report with total order value filtered by a range of dates:
SELECT C.ID, C.NAME , SUM(O.TOTAL_PRICE)
FROM CUSTOMERS C
JOIN ORDERS O ON O.CUSTOMER_ID = C.ID
WHERE O.DATE BETWEEN #value1 AND #value2
GROUP BY C.ID, C.NAME
this works OK, but I want to select last 3 year sums of total orders value grouped by customer, this is the results that I want:
1Year | 2Year | 3Year | Customer_Name
-------------------------------------------------
XXX | YYY | ZZZZ | Customer1
XYX | YYZ | ZZTZ | Customer2
....
I've this cardinality:
Customer table with 22.000 rows
Orders table with 87.000 rows
Orders details with 600.000
It is possible without temptable,vartable or stored procedure with long execution time?
In my report I want also to calculate total Qty of last 3 years grouped by customer of a product, but this is the next step.
Any ideas?
Thanks

You can use a case statement to get the result you want. Since there is some ambiguity in your post about how the year ranges are defined, I've left out any calculations to get those year end/starts and just put variables in. You can revise to suit your need.
SELECT C.ID
,C.NAME
,SUM(CASE
WHEN o.DATE BETWEEN #year1start
AND #year1end
THEN O.TOTAL_PRICE
ELSE 0
END) Year1
,SUM(CASE
WHEN o.DATE BETWEEN #year2start
AND #year2end
THEN O.TOTAL_PRICE
ELSE 0
END) Year2
,SUM(CASE
WHEN o.DATE BETWEEN #year3start
AND #year3end
THEN O.TOTAL_PRICE
ELSE 0
END) Year3
FROM CUSTOMERS C
INNER JOIN ORDERS O ON O.CUSTOMER_ID = C.ID
GROUP BY C.ID
,C.NAME

Another option is to use pivot statement. I assume every your date range equals to one year (e.g. 2013, 2014 and so on).
If these years are strongly determined pivot isn't very beautiful option (look at full sqlfiddle example, it has possible solution for your additional question):
select
c.Id, c.Name, c.Address, CostByYear.[2013], CostByYear.[2014], CostByYear.[2015]
from Customers c
left join (
select
pt.Customer_Id, isnull(pt.[2013], 0) as [2013],
isnull(pt.[2014], 0) as [2014], isnull(pt.[2015], 0) as [2015]
from (
select
o.Customer_Id, year(o.Date) [Year], sum(o.Total_Price) [TotalCost]
from Orders o
group by
o.Customer_Id, year(o.Date)
) src
pivot (
sum(TotalCost) for [Year] in ([2013], [2014], [2015])
) pt
) CostByYear on
c.Id = CostByYear.Customer_Id
order by
c.Name
Also you can do both approaches (mine and prev answer) with dynamically created queries if year ranges aren't known and strongly defined.

Related

SQL: How to return revenue for specific year

I would like to show the revenue for a specific year for all customers regardless of whether or not they have revenue data for the specific year. (in cases they dont have data for the specific year, a filler like 'no data' would work)
Sample Data looks like:
Table 1
Customer
Price
Quantity
Order Date
xxx
12
5
1990/03/25
yyy
15
7
1991/05/35
xxx
34
2
1990/08/21
Desired Output would look a little something like this:
Customer
Revenue (for 1990)
xxx
128
yyy
no data
Getting the total revenue for each would be:
SELECT Customer,
SUM(quantity*price) AS Revenue
but how would i go about listing it out for a specific year for all customers? (incl. customers that dont have data for that specific year)
We can use a CTE or a sub-query to create a list of all customers and another to get all years and the cross join them and left join onto revenue.
This gives an row for each customer for each year. If you add where y= you will only get the year requested.
CREATE TABLE revenue(
Customer varchar(10),
Price int,
Quantity int,
OrderDate date);
insert into revenue values
('xxx', 12,5,'2021-03-25'),
('yyy', 15,7,'2021-05-15'),
('xxx', 34,2,'2022-08-21');
with cust as
(select distinct customer c from revenue),
years as
(select distinct year(OrderDate) y from revenue)
select
y "year",
c customer ,
sum(price*quantity) revenue
from years
cross join cust
left join revenue r
on cust.c = r.customer and years.y = year(OrderDate)
group by
c,y,
year(OrderDate)
order by y,c
year | customer | revenue
---: | :------- | ------:
2021 | xxx | 60
2021 | yyy | 105
2022 | xxx | 68
2022 | yyy | null
db<>fiddle here
You would simply use group by and do the sum in a subquery and left join it to your customers table. ie:
select customers.Name, totals.Revenue
from Customers
Left join
( select customerId, sum(quantity*price) as revenue
from myTable
where year(orderDate) = 1990
group by customer) totals on customers.CustomerId = myTable.customerId;

SQL Query to get value of recent order alongwith data from other tables

I am writing an SQL query to get data from more than 3 tables, but for simplifying the question here I am using a similar scenario with 3 tables.
Table1 Customer (PK-CustomerID, Name)
CustomerID
Name
1
John
2
Tina
3
Sam
Table2 Sales (FK-Id, SalePrice)
ID
SalePrice
1
200.00
2
300.00
3
400.00
Table3 Order (PK-Id, FK-CustomerID, Date, Amount)
Id
CustomerID
Date
Amount
101
1
25-09-2021
30.0
102
1
27-09-2021
40.0
103
2
19-09-2021
60.0
In the output, Date and Amount should be the from most recent Order (latest Date), for a customer
My approach was
Select c.CustomerID, c.Name, s.SalePrice, RecentOrder.Date, RecentOrder.Amount from
Customer as c
LEFT JOIN Sales s ON c.CustomerID = s.ID
LEFT JOIN (SELECT top 1 o.Date, o.Amount, o.CustomerID
FROM Order o, Customer c1 WHERE c1.CustomerID = o.CustomerID ORDER BY o.Date DESC)
RecentOrder ON c.CustomerID = RecentOrder.CustomerID
Output I get
CustomerID, Name, SalePrice, Date, Amount
CustomerID
Name
SalePrice
Date
Amount
1
John
200.00
27-09-2021
40.0
2
Tina
300.00
null
null
3
Sam
400.00
null
null
The output I get includes the most recent order out of all the orders. But I want to get the recent order out of the orders made by that customer
Output Required
CustomerID, Name, SalePrice, Date, Amount
CustomerID
Name
SalePrice
Date
Amount
1
John
200.00
27-09-2021
40.0
2
Tina
300.00
19-09-2021
60.0
3
Sam
400.00
null
null
Instead of subquery in left join, you can check with outer apply.
Check following way
Select c.CustomerID, c.Name, s.SalePrice, RecentOrder.Date, RecentOrder.Amount
from Customer c
LEFT JOIN Sales s ON c.CustomerID = s.ID
OUTER APPLY (
SELECT top 1 o.Date, o.Amount, o.CustomerID
FROM [Order] o
WHERE o.CustomerID = c.CustomerID ORDER BY o.Date DESC) RecentOrder`
You need to pre-aggregate or identify the most recent order for each customer order, your query is selecting 1 row for all orders.
Try the following (untested!)
select c.CustomerID, c.Name, s.SalePrice, o.Date, o.Amount
from Customer c
left join Sales s on c.CustomerID = s.ID
outer apply (
select top (1) date, amount
from [order] o
where o.CustomerId=c.CustomerId
order by Id desc
)o

SQL select logic two tables

I have two tables
Customer(ID, First_Name, Last_Name, Address);
Orders (ID, Product_Name, PRICE, Order_Date DATE, Customer_ID, Amount);
I must select last names of the customers along with the count of their orders.
output of select request must be
SMITH | 0
GREG | 2
WATSON | 0
HOLMSE | 2
RUST | 4
FRINGE | 1
TKACH | 3
You can use the following, using a LEFT JOIN and GROUP BY:
SELECT c.Last_Name, COUNT(o.ID)
FROM Customer c LEFT JOIN Orders o ON c.ID = o.Customer_ID
GROUP BY c.ID
SELECT
c.Last_Name, COUNT(o.ID)
FROM
Customer c
LEFT JOIN
Orders o ON c.ID = o.Customer_ID
GROUP BY c.Last_Name
ORDER BY c.Last_Name;
You can do this with left join and group by on Last_Name columns.

Query to pull previous order date corresponding to a customer?

I have a schema with Customer table and Order table. A customer can place order in multiple dates. I need to have previous order_date for every order_date corresponding to a customer.
Say a customer placed 4 orders, then for newest order(4th order) - it must pull current order_date and previous order_date(3rd order). For 3rd order placed by customer, it must pull 3rd order_date as current order_date and previous order_date(2nd order) as so on.
I am using below query to get previous order_date and then joining with current_query to get result::
select customerid, orderid, order_date as previous_order_date
from (
select c.customerid, o.orderid, o.order_date,
row_number() over (partition by c.customerid, o.orderid
order by o.order_date) rown
from customers c join orders o on c.customerid = o.customerid
) a
where rown = 2
But the issue is, I am getting a single date corresponding to a customerid whereas the requirement is - just previous order_date corresponding to current order_date for a customer.
Any suggestion would help! Thanks
Try with LAG() window function per customerid:
select
c.customerid, o.orderid, o.order_date,
lag(o.order_date) over (partition by c.customerid order by o.order_date) AS prev_order_date
from customers c
join orders o on c.customerid = o.customerid
For the earliest order of every customer prev_order_date will be null.
Sample result (don't mind orderid, it's just for the example):
customerid | orderid | order_date | prev_order_date
------------+---------+------------+-----------------
1 | 6 | 2015-02-08 |
1 | 2 | 2016-02-05 | 2015-02-08
1 | 3 | 2016-02-08 | 2016-02-05
1 | 1 | 2016-03-05 | 2016-02-08
2 | 5 | 2016-07-01 |
2 | 4 | 2016-07-08 | 2016-07-01
If one customer can place the same order within different dates (weird, but this seems to be your case) add o.orderid to the PARTITION BY clause.
Unfortunately, LAG() didn't work when used in SQL node for reporting purpose. I tried using below query and got desired result:
SELECT c.customer_code, o.customer_sid, o.order_id, o.order_no,
o.order_created_date,
(SELECT MAX (o1.order_created_date)
FROM d_customer c1 LEFT JOIN f_order o1
ON c1.customer_sid =
o1.customer_sid
WHERE c1.customer_sid = c.customer_sid
AND o1.order_created_date < o.order_created_date
AND EXISTS (SELECT 1
FROM f_invoice i
WHERE i.order_id = o1.order_id))
AS prev_order_created_date,
t.financial_year, t.financial_month_no
FROM d_customer c JOIN f_order o
ON c.customer_sid = o.customer_sid
AND c.customer_type = 'PATIENT'
AND c.customer_country = 'UNITED STATES'
AND o.customer_type = 'PATIENT'
AND o.bill_to_country = 'UNITED STATES'
AND o.order_status = 'SHIPPED'
AND o.order_type = 'SALES'
AND o.order_group = 'REVENUE'
-- AND c.customer_code = '233379PT'
LEFT JOIN d_time t ON t.time_sid = o.order_created_date_sid
ORDER BY order_created_date DESC

SQL Sum of count across 3 tables

Say you have 3 tables:
Categories
Category ID
Name
Products
Product ID
Category ID (FK)
Sales
Product ID (FK)
Sale date
I'm trying to come up with a query that will result in a data table that shows the total number of sales per category, like:
Cat ID | Name | Total Sales
-------|------|-------------
1 | Red | 35
2 | Blue | 25
For bonus points, add a WHERE clause to select within a specific date range on the 'Sale Date' column.
I'm using SQL Server.
For SQL-Server could be something like that:
SELECT c.CategoryID AS [Cat ID],
c.Name AS [Name],
COUNT(s.ProductID) AS [Total Sales]
FROM Categories c
LEFT JOIN Products p
ON c.CategoryID = p.CategoryID
LEFT JOIN Sales s
ON p.ProductID = s.ProductID
WHERE s.Saledate BETWEEEN startDate and endDate
GROUP BY c.CategoryID, c.Name
select Categories.CatID, Categories.Name, count(*) as TotalSales
from Categories
join Products ON Categories.CategoryID = Products.CategoryID
join Sales ON Products.ProductID = Sales.ProductID
WHERE Sales.Saledate BETWEEEN date1 and date2
GROUP BY Categories.CatID, Categories.Name