How to apply aggregate function only on distinct records - mysql

I have two tables, orders and order_item.
orders table:
Id Total DeliveryCharge Status DeliveryDate
2001 600 120 30 2015-09-01 11:56:32
2002 1500 150 30 2015-09-09 09:56:32
2003 1200 100 30 2015-09-30 08:05:32
order_item table:
Id OrderTotal Quantity
12001 2001 2
12002 2001 1
12003 2002 1
12004 2003 1
12005 2003 1
As each order can contain multiple products, that way order_item table could multiple records for a single order.
I want to get result by the query is
OrderCount Quantity OrderTotal DeliveryCharge
3 6 3300 370
I wrote a query
select count(distinct od.Id) as OrderCount,
sum(oi.Quantity) as Quantity,
(select sum(ord.OrderTotal) from orders ord
where ord.DeliveryDate between '2015-09-01' and '2015-10-01' and ord.Status=30 ) as OrderTotal
from orders od
join Order_items oi on od.Id=oi.orderId
where od.Status=30
and od.DeliveryDate between '2015-09-01' and '2015-10-01'
which has the result
OrderCount Quantity OrderTotal
3 6 3300
But now I want the sum of DeliveryCharge of orders table, so again I have to write select sub-query as I wrote for OrderTotal.
Is there a good way to find it with single query without using multiple sub-queries?

Put subqueries in the from clause:
select o.OrderCount, o.OrderTotal, o.OrderDeliveryCharge, oi.quantity
from (select count(*) as OrderCount, sum(Total) as OrderTotal,
sum(DeliveryCharge) as OrderDeliveryCharge
from orders
) o cross join
(select sum(quantity) as quantity
from order_item
) oi;

Use this
SELECT SUM(oi.oc) AS 'OrderCount', SUM(oi.q) AS 'Quantity', SUM(o.total) AS 'OrderTotal', SUM(o.deliverycharge) AS 'DeliveryCharge'
FROM
orders o INNER JOIN
(SELECT ordertotal, COUNT(DISTINCT(ordertotal)) AS oc, SUM(quantity) AS q FROM order_item GROUP BY 1) oi ON o.id=oi.ordertotal

Related

How to group rows in SQL by earliest date when are there are multiple rows with earliest date?

I am trying to come up with a query that will return the aggregate data for the earliest orders the customers have placed. What I cannot quite wrap my head around is how to construct this query when there are multiple orders placed on the same day for the earliest purchase date for customer 2.
customers
id
name
created_at
1
Sam
2019-07-12
2
Jimmy
2019-01-22
items
id
name
price
1
Watch
200
2
Belt
75
3
Wallet
150
orders
id
customer_id
item_id
created_at
1
1
1
2018-08-01
2
1
2
2018-08-11
3
2
1
2019-01-22
4
2
3
2019-01-22
5
2
2
2019-03-03
expected query
customer_id
name
first_purchase_date
n_items
total_price
1
Sam
2018-08-01
1
200
2
Jimmy
2019-01-22
2
350
I currently have the following query set up, but this query is grouping by the customer_id such that the total number of items and total price do not reflect the earliest orders.
SELECT
orders.customer_id,
customers.name AS name,
MIN(orders.created_at) AS first_purchase_date,
COUNT(*) as n_items,
SUM(items.price) as total_price
FROM orders
INNER JOIN customers
ON orders.customer_id = customers.id
INNER JOIN items
ON orders.item_id = items.id
GROUP BY
customers.id
my incorrect query
customer_id
name
first_purchase_date
n_items
total_price
1
Sam
2018-08-01
2
275
2
Jimmy
2019-01-22
3
425
I recreated the tables in a SQL Server environment but this should help...I hope as it gives you the query result you're looking for. The data is exactly the same but I'm using temporary tables so hence the # prefixes.
SELECT
#orders.customer_id,
#customer.name AS name,
#orders.created_at as first_purchase_date,
--MIN(#orders.created_at) AS first_purchase_date,
COUNT(*) as n_items,
SUM(#items.price) as total_price
FROM #orders
INNER JOIN #customer
ON #orders.customer_id = #customer.id
INNER JOIN #items
ON #orders.item_id = #items.id
inner join
(
select customer_id, name, MIN(first_purchase_date) as
first_purchase_date
from
(
SELECT
#orders.customer_id,
#customer.name AS name,
#orders.created_at as first_purchase_date,
--MIN(#orders.created_at) AS first_purchase_date,
COUNT(*) as n_items,
SUM(#items.price) as total_price
FROM #orders
INNER JOIN #customer
ON #orders.customer_id = #customer.id
INNER JOIN #items
ON #orders.item_id = #items.id
group by #orders.customer_id,#customer.name, #orders.created_at
)base
group by customer_id, name
) firstorders
on
#customer.id = firstorders.customer_id
and
#customer.name = firstorders.name
and
#orders.created_at = firstorders.first_purchase_date
group by
#orders.customer_id,#customer.name, #orders.created_at

Getting sum of two rows in an inner joined table

I have these two tables;
trips
id
date
revenue
1
01/01/2020
5000
2
01/01/2020
3000
3
02/01/2020
4000
4
02/01/2020
2000
expenses
id
tripid
amount
1
1
500
2
1
300
3
2
400
4
2
200
5
2
700
I would like to get the sum of revenue collected in a day AND sum of expenses in a day. I have the following sql which gives me results but the sums are entirely wrong.
SELECT i.id, sum(i.revenue) as total, i.date trip , sum(c.amount) as exp, c.tripid expenses FROM trip i INNER JOIN expenses c ON i.id = c.tripid GROUP BY i.date ORDER BY trip DESC
You can preaggregate the expenses by trip, and then aggregate again in the outer query:
select t.date, sum(t.revenue) as revenue, coalesce(sum(e.expense), 0) as expense
from trips t
left join (
select tripid, sum(amount) as expense
from expenses
group by tripid
) e on e.tripid = t.id
group by t.date

How to sum columns from two related tables

I have two tables. Invoices and invoice_items. I am trying to get the sum of total_payment and total quantity for each client. I tried this query:
SELECT client_id,
sum(total_amount), sum(it.quantity) as days_hired
FROM `invoices` `iv`
join invoice_items it on it.invoice_id=iv.invoice_id
group by client_id, it.invoice_id
But for client_id 14, I am getting total payment as 1908 instead of 636. Looks like the sum of this column gets repeated for every invoie_item. Any help will be appreciated.
invoices
invoice_id client_id total_payment
36 13 530
38 14 636
invoice_items
invoice_id user_id quantity
36 2 2
38 3 2
38 4 2
38 5 2
Expected output:
13 530 2
14 636 6
You can try below -
SELECT client_id, sum(total_amount) as toal_amount, sum(it.quantity) as total_quantity
FROM `invoices` `iv`
join
(
select invoice_id,sum(quantity) as quantity from invoice_items group by invoice_id
)it on it.invoice_id=iv.invoice_id
group by client_id, it.invoice_id
Try:
select i.*, ii.total_quantity
from invoices i
join (
select invoice_id, sum(quantity) total_quantity from invoice_items
group by invoice_id
) ii on i.invoice_id = ii.invoice_id

Query orders table and get most recent record for each customer

I have a table of orders. Customers can appear multiple times. The orderID column is an auto increment. I need to run a query to get the most recent order for each customer BUT I need to get the orderID, orderDate and orderProduct of their latest order.
customer orderID orderDate orderProduct
1 1 2015-01-01 shoes
1 2 2015-02-01 food
1 3 2015-03-01 drinks
2 4 2015-01-01 water
2 5 2015-04-01 beer
3 6 2015-01-01 pizza
3 7 2015-07-01 pasta
I had hoped to use:
select orders.*, max(orderDate) as latestOrder from orders group by customer
But this doesn't seem to give me what I need.
The results I am looking for would be:
customer orderID orderDate orderProduct
1 3 2015-03-01 drinks
2 5 2015-04-01 beer
3 7 2015-07-01 pasta
use some kind of self-join here
select t1.* from orders t1
inner join (
select customer, max(orderDate) as latestOrder from orders
group by customer
) t2
where t1.customer = t2.customer AND t1.orderDate = t2.latestOrder
WITH CTE AS(
SELECT customer,orderID,orderDate,orderProduct,
ROW_NUMBER()OVER(PARTITION BY customer ORDER BY orderID desc)
FROM tab
)
select * from tab where orderid in (
select max(orderid) as maxorder from cte group by customer)
This should work with any RDBMS that supports CTEs. Heres a demo

MySQL query to return MIN() and MAX() of different columns for multiple rows

I currently have a prices table with the following layout:
id codename price discount timestamp
1 1234 599 50 2015-06-10 00:00:00
2 1234 1099 25 2015-06-11 00:00:00
3 3344 199 33 2015-06-12 00:00:00
4 5565 2499 0 2015-06-13 00:00:00
5 5565 1299 50 2015-06-14 00:00:00
I need an SQL query that will give me a single row for each codename. Each row must contain the codename, then the lowest price (along with the associated discount and timestamp for that price), as well as the latest timestamp (again with the associated price and discount for that timestamp)
Desired output:
codename minTimePrice minTimeDis minTime latestPrice latestPriceDis latestPriceTime
1234 599 50 2015-06-10 00:00:00 1099 25 2015-06-11 00:00:00
3344 199 33 2015-06-12 00:00:00 199 33 2015-06-12 00:00:00
5565 1299 50 2015-06-14 00:00:00 1299 50 2015-06-14 00:00:00
EDIT: So I have gotten to where I can have the 2 seperate queries, one gets the row with the MIN(price) and the second gets the row with the MAX(timestamp) for each codename.
Now what I need to do is join them together so that they are all on the same row (grouped by codename) as in the example above.
SQL Fiddle of 2 queries
So after some playing with joins I was able to get the 2 queries to output onto a single row per codename:
SELECT *
FROM
(
SELECT p.*
FROM prices p
JOIN
(
SELECT codename, MIN(price) minPrice
FROM prices GROUP BY codename
) p2
ON p.price = p2.minPrice AND p.codename = p2.codename
) min
LEFT JOIN
(
SELECT p.*
FROM prices p
JOIN
(
SELECT codename, MAX(timestamp) maxTime
FROM prices GROUP BY codename
) p2
ON p.timestamp = p2.maxTime AND p.codename = p2.codename
) latest
ON latest.codename = min.codename
I'm sure the query is far from perfect, but it does give me the results I am looking for.
SQL Fiddle
If there is anything drastically wrong with this, please let me know and I can update.
Try following,
Select codename, minPrice, minDis, minTime, latestPrice, latestDis, latestTime from
(
Select T_Low.codename, minPrice, minDis, minTime, T_Latest.latestPrice, T_Latest.latestDis, T_Latest.latestTime from
(
select * from (
select row_number() over(partition by codename order by codename, price) row_id, codename, price as minPrice, discount as minDis, timestamp as minTime from
(
select codename, discount, timestamp , min(price) as price from prices
group by codename, discount, timestamp
)T
) T1
where row_id = 1
) T_Low
left join
(
select * from (
select row_number() over(partition by codename order by codename, timestamp desc) row_id, codename, price as latestPrice, discount as latestDis, timestamp as latestTime from
(
select codename, discount, timestamp , min(price) as price from prices
group by codename, discount, timestamp
)T
) T1
where row_id = 1
)t_Latest
ON T_Low.codename= T_Latest.codename and T_Low.row_id = T_Latest.row_id
)T
order by codename