The following statement surprisingly works, but I'm not sure joining the same table 3 times is efficient. I had to disable ONLY_FULL_GROUP_BY in order for it to work.
There are 2 tables in play. One is the main table with Distributor information, the second is a table of purchases that contains the amount, date, and id of the associated Distributor in the main table (assoc).
There are 3 things I needed. Year to date sales, which SUMS the amount of a certain Distributor's sales from the current year. Last year sales, which does the same for the previous year. Then finally just get the latest purchase date and amount.
The user needs to be able to filter by these values (lys, ytd, etc...) so joining them as variables seems like the way to go. The DB size is about 7,000 records.
SELECT
d.*,
ytd_total,
lys_total,
last_amount,
last_purchase
FROM Distributor as d
LEFT JOIN (
SELECT
assoc, SUM(amount) ytd_total
FROM purchases
WHERE db = 1 AND purchase_date >= '{$year}-01-01'
GROUP BY assoc
) AS ytd
ON ytd.assoc = d.id
LEFT JOIN (
SELECT
assoc, SUM(amount) lys_total
FROM purchases
WHERE db = 1 AND purchase_date BETWEEN '{$lyear}-01-01' AND '{$lyear}-12-31'
GROUP BY assoc
) AS lys
ON lys.assoc = d.id
LEFT JOIN (
SELECT
assoc, amount last_amount, purchase_date last_purchase
FROM purchases
WHERE db = 1
GROUP BY assoc
) AS lst
ON lst.assoc = d.id
WHERE ........
You can do more work in each aggregation query. I think this is more whatyou want:
select d.*, pa.ytd_total, pa.lys_total, pa.last_purchase_date, p.amount
from distributor d left join
(select p.assoc,
sum(case when p.purchase_date >= '{$year}-01-01' then p.amount end) as ytd_total,
sum(case when p.purchase_date BETWEEN '{$lyear}-01-01' AND '{$lyear}-12-31' then p.amount end) as lys_total,
max(p.purchase_date) as last_purchase_date
from purchases p
where p.db = 1
group by p.assoc
) pa left join
purchases p
on pa.assoc = p.assoc and pa.last_purchase_date = p.purchase_date;
Related
So I have a database like this:
product_table: (product_id, name),
date_table: (date_id, day, month, year),
sales_table: (sale_id, product_id, date_id, total_value)
I need to get the most sold product for every month that exists in the "date_table" with one query, the only way I was able to do was specifing the month and year in the query, but then i gotta do one query for each month which is not what I need.
Edit:
here is the query i have written:
SELECT p.name, d.month, d.year, SUM(s.total_value) as total
FROM sales_table s
INNER JOIN product_table p ON s.product_id = p.product_id
INNER JOIN date_table d ON s.date_id = d.date_id
WHERE d.month = "$month" AND d.year = "$year"
ORDER BY total DESC
LIMIT 1
As i said, i have to give the month and the year i want here, but i need the most sold product (with the use of sales value in sales_table not the row count of product table) for every month in just one query!
You can use row_number() with aggregation. Assuming that "most sold" refers to the count in the sales table:
select sd.*
from (select d.year, d.month, s.product_id, count(*) as cnt,
row_number() over (partition by d.year, d.month order by count(*) desc) as seqnum
from sales s join
dates d
on s.date_id = d.date_id
group by d.year, d.month
) sd
where seqnum = 1;
I have the following scenario. i have three tables (users, sales, sales_details) Users to Sales is a 1 to 1 relationship and sales to sales_details is 1 to many.
I am running a query where I get all the sales for each user by joining all 3 tables without any issue.
Query looks something like this
SELECT s.month as month,u.name as name, s.year as year, s.date as date,sum(sd.qty) as qty,sum(sd.qty*sd.value) as value,s.id as id,sum(sd.stock) as stock,s.currency as currency,s.user as user
FROM sales as s
left join sales_details as sd on s.id = sd.Sales
inner join users as u on s.user = u.Id
group by s.Id
What I want to do now is add an extra field in my query which will be a subquery.
SELECT SUM(total) AS total_yearly
FROM (
SELECT sum(qty) as total
FROM sales
left join sales_details on sales.Id = sales_details.Sales
WHERE ((month <= MONTH(NOW()) and year = YEAR(NOW()))
or (month >= MONTH(Date_add(Now(),interval - 12 month)) and year = YEAR(Date_add(Now(),interval - 12 month))))
and User = **ID OF USER** ) as sub
This query on its own gives me the sales for the user for the past 12 months while the original query does it per month. I know that the result will be the same for each user but i need it for other calculations.
My problem is how I will join the 2 queries so that the subquery will read the user id from the original one.
Thanks in advance!
Group the second query by user, and then join it with the original query.
SELECT s.month as month,u.name as name, s.year as year, s.date as date,
sum(sd.qty) as qty,sum(sd.qty*sd.value) as value,s.id as id,
sum(sd.stock) as stock,s.currency as currency,s.user as user,
us.total
FROM sales as s
left join sales_details as sd on s.id = sd.Sales
inner join users as u on s.user = u.Id
inner join (
SELECT User, sum(qty) as total
FROM sales
left join sales_details on sales.Id = sales_details.Sales
WHERE ((month <= MONTH(NOW()) and year = YEAR(NOW()))
or (month >= MONTH(Date_add(Now(),interval - 12 month)) and year = YEAR(Date_add(Now(),interval - 12 month)))))
GROUP BY User) AS us ON s.user = us.user
group by s.Id
I have the following query which works perfectly, but only if each select finds a row.
I've attempted to add IFNULL to return 0 if no rows were found but I'm still not getting the correct return.
SELECT IFNULL(paid_value,0)-IFNULL(ordered_value,0)+IFNULL(credit_value,0) AS account_balance
FROM
(
SELECT customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
) AS orders
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS paid_value
FROM transactions
WHERE customer_id = '1'
GROUP BY customer_id
) as payments
ON orders.customer_id = payments.customer_id
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS credit_value
FROM credits
WHERE customer_id = '1'
GROUP BY customer_id
) as credits
ON orders.customer_id = credits.customer_id
This query currently returns empty, it's not returning NULL or 0.
When I run
SELECT customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
It also returns empty, not NULL or 0, unless there's a row. In order for the full query to work, each of the 3 separate queries need to have a row in them.
Any ideas?
It if because none of the columns have a result set, so an empty result set is returned, if you want to always display a row in any case you can try with some tricks like this one for example :
SELECT IFNULL(SUM(payments.paid_value),0)-IFNULL(SUM(orders .ordered_value),0)+IFNULL(SUM(credits.credit_value),0) AS account_balance
FROM
(SELECT 1 AS idx, 0 AS paid_value, 0 AS ordered_value, 0 AS credit_value) a
LEFT JOIN
(
SELECT 1 AS idx, customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
) AS orders
ON a.idx = orders.idx
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS paid_value
FROM transactions
WHERE customer_id = '1'
GROUP BY customer_id
) as payments
ON orders.customer_id = payments.customer_id
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS credit_value
FROM credits
WHERE customer_id = '1'
GROUP BY customer_id
) as credits
ON orders.customer_id = credits.customer_id
GROUP BY a.idx
The example is a proof of concept that can be adapted even to other situations where you need to always returns a row with default values even with no elements in underlying tables .
If you want row event if there is no data available, then I suppose you should either have UNION. Or In this scenario you can include you Customer Table & put each Table on Right Side, make a LEFT JOIN.
I think following select will give you an IDEA.
SELECT O.customer_id, SUM(O.order_total) AS ordered_value
FROM Customers C
left join orders O on C.ID =o.customer_id
WHERE O.customer_id = '1'
GROUP BY O.customer_id
I have an orders table which consists of the following:
id
order_total
delivery_cost
customer_id
I also have a transactions table which has:
id
amount
customer_id
status
What I'm trying to do is,
SELECT SUM(order_total + delivery_cost) FROM orders WHERE customer_id = '1'
then
SELECT SUM(amount) FROM transactions WHERE customer_id = '1' AND transaction_status = 'Paid'
Then with the data, minus the total amount from the order totals.
I've tried different queries using JOINS, but I just can't get my head around it, for example:
SELECT SUM(OrdersTotal - TransactionTotal) as AccountBalance
FROM (
SELECT SUM(`order_total` + `delivery_cost`) FROM `orders` as OrdersTotal
UNION ALL
SELECT SUM(`amount`) FROM `transactions` WHERE `transaction_status` = 'Paid' as TransactionTotal
)
but this didn't work at all. Any help would be greatly appreciated.
The two datasets are effectively autonomous but assuming that it's unlikely to have transactions for a customer without orders, you can acheive your result with a LEFT JOIN rather than a ful outer join - but if you simply join the base tables then you'll likely get values from one table repeated in the interim result set (this is why Joseph B's answer is wrong when a customer has something other than a single row in each table).
SELECT ordered_value-IFNULL(paid_value,0) AS acct_balance
FROM
(
SELECT customer_id, SUM(order_total + delivery_cost) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
) AS orders
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS paid_value
FROM transactions
WHERE customer_id = '1'
AND transaction_status = 'Paid'
FROUP BY customer_id
) as payments
ON orders.customer_id = payments.customer_id
(here the 'GROUP BY' and 'ON' clauses are redundant since your only looking at a single customer - but are required for multiple customers).
Note that calculating a balance based on a sum of transactions is technically correct, it does not scale well - for large systems a better solution (although it breaks the rules of normalization) is to maintain a unified table of transactions and a balance for for the account along with each transaction amount - alternatively use checkpointing.
You can just name the column in your union all query and sum on that:
SELECT SUM(col) as AccountBalance
FROM (SELECT SUM(`order_total` + `delivery_cost`) as col FROM `orders` as OrdersTotal
UNION ALL
SELECT SUM(`amount`) FROM `transactions` WHERE `transaction_status` = 'Paid' as TransactionTotal
) t;
Try this query using a JOIN:
SELECT
SUM(o.order_total + o.delivery_cost) - SUM(t.amount) AS AccountBalance
FROM orders o
INNER JOIN transactions t
ON o.customer_id = t.customer_id AND o.customer_id = '1' AND t.transaction_status = 'Paid';
This should give you the account balance for each customer?
SELECT
ISNULL(o.customer_id, t.customer_id) AS customer_id
OrdersTotal - TransactionTotal AS AccountBalance
FROM (
SELECT
customer_id,
SUM(order_total + delivery_cost) AS OrdersTotal
FROM
orders
GROUP BY
customer_id) o
FULL OUTER JOIN (
SELECT
customer_id,
SUM(amount) AS TransactionTotal
FROM
transactions
WHERE
transaction_status = 'Paid'
GROUP BY
customer_id) t
ON t.customer_id = o.customer_id
You can join the results of your queries and perform your calculations ,note sum without group by will result as one row so the sum in outer query doesn't mean when you have only one row and according to your logic of calculation union has nothing to do with it
SELECT t1.OrdersTotal - t2 .TransactionTotal AS AccountBalance
FROM (
SELECT SUM(order_total + delivery_cost) OrdersTotal ,customer_id
FROM orders
WHERE customer_id = '1'
) t1
JOIN (
SELECT SUM(amount) TransactionTotal ,customer_id
FROM transactions
WHERE customer_id = '1' AND transaction_status = 'Paid'
) t2 USING(customer_id)
Since you are only concerned about a single customer, just have them listed as two different queries as the source...
select
charges.chg - paid.pay as Balance
from
( SELECT SUM(order_total + delivery_cost) chg
FROM orders
WHERE customer_id = '1' ) charges,
( SELECT SUM(amount) pay
FROM transactions
WHERE customer_id = '1' AND transaction_status = 'Paid' ) paid
Now, if you wanted for all customers to see who is outstanding, add the customer's ID to each query and apply a group by, but then change to a LEFT-JOIN so you get all orders with or
select
charges.customer_id,
charges.chg - coalesce( paid.pay, 0 ) as Balance
from
( SELECT customer_id, SUM(order_total + delivery_cost) chg
FROM orders
group by customer_id ) charges
LEFT JOIN ( SELECT customer_id, SUM(amount) pay
FROM transactions
where transaction_status = 'Paid'
group by customer_id ) paid
on charges.customer_id = paid.customer_id
I think this works best if you try an inline view. In the code block below, check the Group By clause, you might need to add more fields in the Group By depending on what you're selecting in the Inner SELECT statements.Try the code below:
SELECT
SUM(Totals.OrdersTotal-Totals.TransactionTotal)
FROM
(SELECT
SUM(ord.order_total + ord.delivery_cost) AS OrdersTotal
, SUM(trans.amount) AS TransactionTotal
FROM orders ord
INNER JOIN transactions trans
ON ord.customer_id = trans.customer_id
WHERE
ord.customer_id =1
AND trans.transaction_status = 'Paid'
GROUP BY
ord.customer_id
) Totals;
I have one table that lists orders we receive. One row per order.
A second table records transactions associated with the order.
I am trying to produce a report that shows one order per row and on each row to display the total value of the various types of transactions.
My query is:
select
orders.orderID,
customers.username,
(select sum(amount) from transactions where transactions.orderID=orders.orderID and transactionType IN (17) ) cost,
(select sum(amount) from transactions where transactions.orderID=orders.orderID and transactionType IN (18,19,20) ) surcharges,
(select sum(amount) from transactions where transactions.orderID=orders.orderID and transactionType IN (21,22) ) payments_received
from orders
left join customers on orders.customerID=customers.customerID
order by orderID
But this is pretty slow. I have indexes on the appropriate columns.
Can I avoid doing three subqueries and instead just run one query that spits out cost, surcharges and payments_received?
Something like this should do it:
SELECT orders.orderid
, customers.username
, Sum(CASE WHEN transactions.transactiontype IN (17 ) THEN transactions.amount END) As cost
, Sum(CASE WHEN transactions.transactiontype IN (18, 19, 20) THEN transactions.amount END) As surcharges
, Sum(CASE WHEN transactions.transactiontype IN (21, 22 ) THEN transactions.amount END) As payments_received
FROM orders
LEFT
JOIN customers
ON customers.customerid = orders.customerid
LEFT
JOIN transactions
ON transactions.orderid = orders.orderid
GROUP
BY orders.orderid
, customers.username
ORDER
BY orders.orderid