I have these columns and data
Sample data:
CREATE TABLE Orders(customerid int, orderdate datetime, orderqty int);
INSERT into Orders(customerid, orderdate, orderqty) VALUES
(1,'2020-11-25',100),(1,'2020-11-27',160),(2,'2020-12-05',3490),
(1,'2020-11-29',293),(2,'2020-12-07',293),(1,'2020-12-01',382);
sqlfiddle: http://sqlfiddle.com/#!9/d90aaf/1/0
From this data, I want to find out the date difference in last 3 rows by each customer.
an example output would be:
customerid, last 3 orders average days between orders,sum of orderqty for last 3 order
1, 2,835
2, 2,3783
I have tried datediff() but I can't achieve this result by that function.
Can't figure out better trick for latest 3 orders. This works, but probably with not good performance with large tables:
SELECT o3.customerid,DATEDIFF(Latest1,IFNULL(Latest3,Latest2))/IF(Latest3 IS NULL,1,2) AS avgDiff,SUM(oQ.orderqty) AS qty3orders FROM
(SELECT o2.*,MAX(ooo.orderdate) AS Latest3 FROM
(SELECT o1.*,MAX(oo.orderdate) AS Latest2 FROM
(SELECT customerid,MAX(orderdate) AS Latest1 FROM Orders GROUP BY customerid) o1
JOIN Orders oo ON o1.customerid=oo.customerid AND oo.orderdate<o1.Latest1 GROUP BY o1.customerid) o2
LEFT JOIN Orders ooo ON o2.customerid=ooo.customerid AND ooo.orderdate<o2.Latest2 GROUP BY o2.customerid) o3
JOIN orders oQ ON o3.customerid=oQ.customerid AND oQ.orderdate>=COALESCE(o3.Latest3,o3.Latest2,o3.Latest1) GROUP BY o3.customerid
Related
I'm having great difficulty writing this query and cannot find any answers online which could be applied to my problem.
I have a couple of tables which looks similar to the below with. Each purchase date corresponds with an item purchased.
Cust_ID
Purchase_Date
123
08/01/2022
123
08/20/2022
123
09/05/2022
123
10/08/2022
123
12/25/2022
123
01/26/2023
The result I am looking for should contain the customers ID, a range of the purchases, the number of consecutive months they had made a purchase (regardless of which day they purchased), and a count of how many purchases they had made in the time frame. The result should look something like the below for my example.
Cust_ID
Min Purchase Date
Max Purchase Date
Consecutive Months
No. Items Purchased
123
08/01/2022
10/08/2022
3
4
123
12/25/2022
01/26/2023
2
2
I have tried using CTEs with querys similar to
WITH CTE as
(
SELECT
PaymentDate PD,
CustomerID CustID,
DATEADD(m, -ROW_NUMBER() OVER (PARTITION BY c.CustomerID ORDER BY
DATEPART(m,PaymentDate)), PaymentDate) as TempCol1,
FROM customers as c
LEFT JOIN payments as p on c.customerid = p.customerid
GROUP BY c.CustomerID, p.PaymentDate
)
SELECT
CustID,
MIN(PD) AS MinPaymentDate,
MAX(PD) AS MaxPaymentDate,
COUNT(*) as ConsecutiveMonths,
FROM CTE
GROUP BY CustID, TempCol1
However, the above failed to properly count consecutive months. When the payment dates matched a month apart (e.g. 1/1/22 - 2/1/22), the query properly counts the consecutive months. However, if the dates do not match from month to month (e.g. 1/5/22 - 2/15/22), the count breaks.
Any guidance/help would be much appreciated!
This is just a small enhancement on the answer already given by ahmed. If your date range for this query is more than a year, then year(M.Purchase_Date) + month(M.Purchase_Date) will be 2024 for both 2022-02-01 and 2023-01-01 as YEAR() and MONTH() both return integer values. This will return incorrect count of consecutive months. You can change this to use CONCAT() or FORMAT(). Also, the COUNT(*) for ItemsPurchased should be counting the right hand side of the join, as it is a LEFT JOIN.
WITH consecutive_months AS
(
SELECT *,
DATEADD(
month,
-DENSE_RANK() OVER (
PARTITION BY CustomerID
ORDER BY YEAR(PaymentDate), MONTH(PaymentDate)
),
PaymentDate
) AS grp_date
FROM payments
)
SELECT
C.CustomerID AS CustID,
MIN(M.PaymentDate) AS MinPaymentDate,
MAX(M.PaymentDate) AS MaxPaymentDate,
COUNT(DISTINCT FORMAT(M.PaymentDate, 'yyyyMM')) AS ConsecutiveMonths,
COUNT(M.CustomerID) AS ItemsPurchased
FROM customers C
LEFT JOIN consecutive_months M
ON C.CustomerID = M.CustomerID
GROUP BY C.CustomerID, YEAR(M.grp_date), MONTH(M.grp_date)
Here's a db<>fiddle
You need to use the dense_rank function instead of the row_number, this will give the same rank for the same months and avoid breaking the grouping column. Also, you need to aggregate for 'year-month' of the grouping date column.
with consecutive_months as
(
select *,
Purchase_Date - interval
dense_rank() over (partition by Cust_ID order by year(Purchase_Date), month(Purchase_Date))
month as grp_date
from payments
)
select C.Cust_ID,
min(M.Purchase_Date) as MinPurchaseDate,
max(M.Purchase_Date) as MaxPurchaseDate,
count(distinct year(M.Purchase_Date), month(M.Purchase_Date)) as ConsecutiveMonthsNo,
count(M.Cust_ID) as ItemsPurchased
from customers C left join consecutive_months M
on C.Cust_ID = M.Cust_ID
group by C.Cust_ID, year(M.grp_date), month(M.grp_date)
See demo on MySQL
You tagged your question with MySQL, while it seems that you posted an SQL Server query syntax, for SQL Server just use dateadd(month, -dense_rank() over (partition by Cust_ID order by year(Purchase_Date), month(Purchase_Date)), Purchase_Date).
See demo on SQL Server.
first ,i need to get the sum of TotalPrice of sport's and music's departments from the first 3 months of 2016,second, i need to get the result of what i wrote before dividing to sum of all TotalPrice at the year of 2016 from all departments, and third- i need to get the first result dividing to sum of all Total price from all over the years.
all this at the same query!
thanks!
the table called Sales and the attributes are: S_id, date, department, totalPrice.
THIS IS MY CHRY :
Select sum(TotalPrice) as sportMusic, sportMusic/sum(TotalPrice)
From Sales
Where (Department="MUSIC" OR Department="SPORT") and
DATE BETWEEN "2016/01/01" AND "2016/03/31"
You can use your query and two more queries as subqueries (also called "derived tables") in your from clause. Cross join the three result rows and use the totals in your select clause. Something along the lines of:
select
ms_2016_q1.total as ms_2016_q1_total,
ms_2016_q1.total / all_2016.total as rate_2016,
ms_2016_q1.total / all_years.total as rate_all
from
(
select sum(totalprice) as total
from sales
where department in ('MUSIC', 'SPORT')
and date between date '2016-01-01' and date '2016-03-31'
) ms_2016_q1
cross join
(
select sum(totalprice) as total
from sales
where date between date '2016-01-01' and date '2016-12-31'
) all_2016
cross join
(
select sum(totalprice) as total
from sales
) all_years;
I have 2 tables
1st: order
columns: id, date, price
2nd: paypal
columns: id, posted_date, amount
the columns date and posted_date contains the full date & time details; day/month/year hrs:minute:seconds
I need to get the data by grouping by the day from the both tables
order.date (day by day)
count all orders from order table for each day
sum of all price records from order table for each day
sum of all amount records from the another table paypal table for the same days
I can't imagine if I should use join, union, union all, or just merge by comma
SELECT DATE(O.`dater`) AS Dates,
COUNT(O.orders) AS Order_count,
SUM(O.price) as Total_Price,
(SELECT SUM(amount) FROM paypal WHERE DATE(O.`dater`)=`posted_date`) AS Total_Amount
FROM orders O
GROUP BY DATE(O.`dater`)
Note:(I have used column dater instead of column date)
Hope this helps.
This one working for me :)
SELECT DATE(O.`date`) AS Dates,
COUNT(O.order) AS Order_count,
SUM(O.price) as Total_Price,
(SELECT SUM(amount) FROM paypal WHERE DATE(O.`date`)=`posted_date`) AS Total_Amount
FROM order O
GROUP BY DATE(O.`date`)
I'm fairly new to SQL and am having difficulty solving a problem.
'What are the total sales across all products for the salespeople that sell at least one unit of each of the five individual products with the highest sales by unit? Make sure that the query returns the total sales dollars in descending order. Only consider sales that take place over the six complete months prior to a #target_date parameter.'
3 tables exist in the DB.
SalesPerson (SalesPersonID,SalesYTD)
SalesOrderHeader (SalesOrderID,OrderDate,ShipDate)
SalesOrderDetail (SalesOrderID,SalesOrderDetailID,OrderQty,ProductID,UnitPrice)
This is where I'm at so far. I need to compile what I have into one statement and make necessary revisions. Please help!
To capture the top 5 highest sales by unit, the following SYNTAX should work:
SELECT
ProductID,
SUM(Orderqty*Unitprice)
FROM SalesOrderDetail
GROUP BY ProductID
WHERE Orderqty >=1
AND COUNT(productID) =5
ORDER BY SUM(Orderqty*Unitprice) DESC
LIMIT 5;
For the target_date parameter, I think it would be something along these lines?
SELECT
SalespersonID AS ‘Sales Representative’,
SalesYTD AS ‘Total Sales’, target_date
FROM Salesperson
WHERE target_date BETWEEN ‘01-DEC-13’ AND ’01-May-14’;
For the top five highest sales, I would rather propose the slightly simplified
select productid, sum(orderqty * unitprice) as sales
from salesorderdetail
group by productid
order by sales desc
limit 5
and for the six months prior to #target_date something like
where orderdate between date_sub(#target_date, interval 6 months) and #target_date
Assuming a FK SalesOrderDetail(SalesPersonID), you can then join the tables and top five sales as
select p.*
from salesperson p
join salesorderheader h on h.salespersionid = p.salespersionid
join salesorderdetail d on d.salesorderid = h.salesorderid
join (select productid, sum(orderqty * unitprice) as sales
from salesorderdetail
group by productid
order by sales desc
limit 5) t5 on t5.productid = d.productid
where h.orderdate between date_sub(#target_date, interval 6 months) and #target_date
order by p.salesytd desc
I have 2 mysql tables:
"Orders" table:
customer_id | money
3 120
5 80
3 45
3 70
6 20
"collecting" table:
customer_id | money
3 50
3 70
4 20
4 90
I want a result like:
"Total" table:
customer_id | Amount
3 115
4 110
5 80
6 20
"Total" table "customer_id" should be singular
Amount = (SUM(All customer orders.money) - SUM(All customer collecting.money))
"Money" can be NULL
"Orders" table can have customer_id and "Collecting" table may not have
Or
"Collecting" table can have customer_id and "Orders" table may not have
How can i write a single query for output "Total" table?
The following returns the result you expect.
SELECT
customer_id,
SUM(amount) as amount
FROM (
SELECT customer_id, SUM(money) as amount
FROM orders GROUP BY customer_id
UNION ALL
SELECT customer_id, SUM(money) * -1 as amount
FROM collecting GROUP BY customer_id
) as tb
GROUP BY customer_id;
customer_id = 4 returns -110, not 110, since it's only in the collecting table.
Example: http://www.sqlfiddle.com/#!2/3b922/5/0
The fastest way is to union your data with the money value being negative on the collecting table:
-- load test data
create table orders(customer_id int, money int);
insert into orders values
(3,120),
(5,80),
(3,45),
(3,70),
(6,20);
create table collecting(customer_id int,money int);
insert into collecting values
(3,50),
(3,70),
(4,20),
(4,90);
-- populate Total table
create table Total(customer_id int,Amount int);
insert into Total
select oc.customer_id,sum(oc.money) Amount
from (
select customer_id,coalesce(money,0) money from orders
union all
select customer_id,coalesce(-money,0) money from collecting
) oc
group by oc.customer_id;
-- return results
select * from Total;
SQL Fiddle: http://www.sqlfiddle.com/#!2/deebc
You need to do this by pre-aggregating the data. If I assume that the orders come first, you can use left outer join:
select o.customer_id, (o.money - coalesce(c.money)) as Amount
from (select o.customer_id, sum(o.money) as money
from orders o
group by o.customer_id
) o left outer join
(select c.customer_id, sum(c.money) as money
from collecting c
group by c.customer_id
) c
on o.customer_id = c.customer_id;
Something like this should work:
SELECT CASE
WHEN c.customer_id IS NULL
THEN o.customer_id
ELSE c.customer_id
END
,sum(o.MONEY) - sum(c.MONEY)
FROM orders o
OUTER JOIN collecting c ON c.customer_id = o.customer_id
GROUP BY 1
Try this:
SELECT customer_id, SUM(o_money - c_money) AS Amount
FROM (
SELECT customer_id, money AS o_money, 0 AS c_money FROM orders
UNION ALL
SELECT customer_id, 0 AS o_money, money AS c_money FROM collecting
) total
GROUP BY customer_id;
According to your description, you need a FULL [OUTER] JOIN, which is a combination of LEFT JOIN and RIGHT JOIN. Many databases don't support FULL JOIN, so you need to use UNION ALL to reach the same effect.