How to write queries to calculate today's due amount? - mysql

I have scheduled payments, so I have these tables:
customer
+id
paymentSchedule
+id
+customer_id
+amount //total price
+dueDate //date to be paid
payments
+id
+date
+customer_id
+paymentSchedule_id
+amount //amount paid, it can be a partial payment
How do I write a query to get Today's due amount by customer.
I mean I need to join the tables (thats my main problem) and then substract the
sum of the payments.mount minus the sum of the scheduledPaymens.amount
but.. how?
Thanks in advance

This is probably not 100%, but should be pretty solid to help you tweak:
SELECT customer_id, (due.amount - paid.amount) as amountDue
FROM
(SELECT customer_id, SUM(amount) as amount
FROM paymentSchedule
WHERE dateDate <= getDate()
and customer_id = #custid) as due
LEFT JOIN
(SELECT customer_id, SUM(amount) as amount
FROM payments
WHERE customer_id = #custid) as paid ON paid.customer_id = due.customer_id

Ok, this is how I understood the problem. I simplified the tables because they where just complicating things, and adding dates is just straight forward.
PaymentSchedule
+----+-------------+-----------------+
| id | customer_id | original_amount |
+----+-------------+-----------------+
| 1 | Tom | 100 |
| 2 | Tom | 200 |
| 3 | Tom | 300 |
| 4 | Moe | 400 |
+----+-------------+-----------------+
Payments
+----+--------------------+-------------+
| id | paymentSchedule_id | paid_amount |
+----+--------------------+-------------+
| 1 | 1 | 70 |
| 2 | 2 | 150 |
| 3 | 2 | 50 |
| 4 | 4 | 300 |
| 5 | 4 | 25 |
+----+--------------------+-------------+
Result of query
+-------------+-------------------+-----------------+-----------+----------------+
| CUSTOMER_ID | PAYMENTSCHEDULEID | ORIGINAL_AMOUNT | TOTALPAID | PENDINGPAYMENT |
+-------------+-------------------+-----------------+-----------+----------------+
| Tom | 1 | 100 | 70 | 30 |
| Tom | 2 | 200 | 200 | 0 |
| Tom | 3 | 300 | 0 | 300 |
| Moe | 4 | 400 | 325 | 75 |
+-------------+-------------------+-----------------+-----------+----------------+
Query with double select
select *, s.original_amount - s.TotalPaid as PendingPayment from (
select
ps.customer_id, ps.id as PaymentScheduleId, ps.original_amount,
coalesce(sum(p.paid_amount), 0) as TotalPaid
from paymentSchedule ps
left join payments p on p.paymentSchedule_id = ps.id
group by ps.customer_id, PaymentScheduleId, ps.original_amount
) as S
Query with single select
select
ps.customer_id, ps.id as PaymentScheduleId, ps.original_amount,
coalesce(sum(p.paid_amount), 0) as TotalPaid,
ps.original_amount - coalesce(sum(p.paid_amount), 0) as PendingPayment
from paymentSchedule ps
left join payments p on p.paymentSchedule_id = ps.id
group by ps.customer_id, PaymentScheduleId, ps.original_amount
The result of both queries is the same. I just wonder which one runs faster. You can try both and tell us :)
Let me know if this this is the result you expected

Something like this should be a good starting point for you to tweak.
SELECT c.*
FROM customer c
INNER JOIN paymentSchedule ps
ON c.id = ps.customer_id
LEFT JOIN payments p
ON ps.id = p.paymentSchedule_id
WHERE ps.dueDate = 'This depends on how you store dueDate'
AND ps.amount - p.amount > 0

Related

How to select the sum() of a group of rows and the sum() of another group

I have created a SQLfiddle demo with sample data and desired result here :(http://sqlfiddle.com/#!9/dfe73a/7)
sample data
-- table company
+--------+---------+
| id | name |
+--------+---------+
| 1 | foo |
| 2 | bar |
+--------+---------+
-- table sales
+--------+---------------+-----------------+
| id | company_id | total_amount |
+--------+---------------+-----------------+
| 1 | 1 | 300.0 |
| 2 | 1 | 300.0 |
| 2 | 1 | 100.0 |
+--------+---------------+-----------------+
-- table moves
+--------+---------------+-----------------+
| id | company_id | balance_move |
+--------+---------------+-----------------+
| 1 | 1 | 700.0 |
| 2 | 1 | -300.0 |
| 2 | 1 | -300.0 |
+--------+---------------+-----------------+
I need to select every company along with the sum of it's total amount of sales and the sum of it's total balance moves
desired result
+----+----------------------+---------------------+
| id | total_amount_sum | balance_move_sum |
+----+----------------------+---------------------+
| 1 | 700 | 100 |
+----+----------------------+---------------------+
| 2 | (null) | (null) |
+----+----------------------+---------------------+
I tried this SQL query
SELECT
company.id,
sum(total_amount) total_amount_sum,
sum(balance_move) balance_move_sum
FROM company
LEFT JOIN sales ON company.id = sales.company_id
LEFT JOIN moves ON company.id = moves.company_id
GROUP BY company.id
But the sum() functions add all the redundant values came from the joins which result in 2100 (700*3) for total amount and 300 (100*3) for net balance
bad SQL statement result
+----+----------------------+---------------------+
| id | total_amount_sum | balance_move_sum |
+----+----------------------+---------------------+
| 1 | 2100 | 300 |
+----+----------------------+---------------------+
| 2 | (null) | (null) |
+----+----------------------+---------------------+
Is it possible to achieve the result I want ?
You're repeating rows by doing your joins.
Company: 1 row per company
After Sales join: 3 rows per company (1x3)
After Moves join: 9 rows per company (3x3)
You end up triplicating your SUM because of this.
One way to fix is to use derived tables like this, which calculate the SUM first, then join the resulting rows 1-to-1.
SELECT
company.id,
total_amount_sum,
balance_move_sum
FROM company
LEFT JOIN (SELECT SUM(total_amount) total_amount_sum, company_id
FROM sales
GROUP BY company_id
) sales ON company.id = sales.company_id
LEFT JOIN (SELECT SUM(balance_move) balance_move_sum, company_id
FROM moves
GROUP BY company_id
) moves ON company.id = moves.company_id
Using sub-queries to calculate the two sums separately will work.
SELECT
company.id,
(Select sum(total_amount) from sales where sales.company_id = company.id) total_amount_sum,
(Select sum(balance_move) from moves where moves.company_id = company.id) balance_move_sum
FROM company

Select SUM from multiple tables for every record in MySQL table

I'm having a table with main invoice data, and two table with invoice items:
items which are based on hourly work, with an hourly rate and an amount of hours
items which are products, with a unit count an unit price
For the invoice overview page, I'd like to retrieve all invoices and their total amounts with one query.
A simplified schema
invoices_main
| invoice_id |
| 1 |
| 2 |
| 3 |
invoices_items_products
| item_id | invoice_id | item_count | item_unit_price |
| 1 | 1 | 1 | 999.95 |
| 2 | 1 | 20 | 49.50 |
| 3 | 2 | 3 | 15.00 |
| 4 | 2 | 5 | 5.00 |
| 5 | 3 | 2 | 150.00 |
invoices_items_hourly
| item_id | invoice_id | item_hours | item_hourly_rate |
| 1 | 1 | 3.50 | 90.00 |
| 2 | 1 | 1.00 | 140.00 |
| 3 | 2 | 12.00 | 90.00 |
| 4 | 3 | 1.50 | 90.00 |
With the help of this question, I've constructed the following query:
SELECT
I.invoice_id,
IFNULL(
SUM(ROUND(P.item_unit_price * P.item_count, 2)),
0
) + IFNULL(
SUM(ROUND(H.item_hourly_rate * H.item_hours, 2)),
0
) AS invoice_total_amount
FROM
invoices_main I
LEFT JOIN invoices_items_products P ON I.invoice_id = P.invoice_id
LEFT JOIN invoices_items_hours H ON I.invoice_id = H.invoice_id
GROUP BY
I.invoice_id
It works kind of, but if an invoice has both products and hourly items, with at least multiple entries for one of both, items are duplicated due to the joins and the total amount becomes way too high.
Thus, in the above example schema, it goes wrong with invoice_id 1 and 2, but work with 3.
How can I retrieve a list of invoices with their respective total amounts, in a way that works even if an invoice has multiple products and multiple hourly items?
Try putting both left join's into a subquery instead.
SELECT
I.invoice_id,
IFNULL
(
(
SELECT SUM(ROUND(H.item_hourly_rate * H.item_hours, 2))
FROM invoices_items_hours AS H
WHERE H.invoice_id = I.invoice_id
)
, 0
) +
IFNULL
(
(
SELECT SUM(ROUND(P.item_unit_price * P.item_count, 2))
FROM invoices_items_products AS P
WHERE P.invoice_id = I.invoice_id
)
, 0
) AS invoice_total_amount
FROM invoices_main AS I
GROUP BY I.invoice_id
As mentioned in the comments, you should sum up the revenue in each table per invoice_id before doing the join. If you're looking to get the revenue from both of these places then you can add (B.unit_revenue + C.hourly_revenue) total_revenue to the first SELECT statement below.
SELECT A.invoice_id, B.unit_revenue, C.hourly_revenue FROM
invoices_main AS A
JOIN (
SELECT invoice_id, SUM(item_count * item_unit_price) unit_revenue
FROM invoices_items_products GROUP BY invoice_id
) B
ON
A.invoice_id = B.invoice_id
JOIN (
SELECT invoice_id, SUM(item_hours * item_hourly_rate) hourly_revenue FROM
invoices_items_hours GROUP BY invoice_id
) C
ON
A.invoice_id = C.invoice_id

How to SUM column in MySQL left join

So i have table cont_selling
---------------------------------
cont_selling_id | date |
---------------------------------
1 | 2015-05-24 |
2 | 2015-06-06 |
---------------------------------
table 02 cont_sold
----------------------------------------------------
cont_sold_id | cont_selling_id | price |
---------------------------------------------------
1 | 1 | 10 |
2 | 1 | 10 |
3 | 1 | 30 |
4 | 2 | 20 |
5 | 2 | 10 |
--------------------------------------------------
and table 03 payment
----------------------------------------------
payment_id | cont_selling_id | paid |
-----------------------------------------------
1 | 1 | 10 |
2 | 2 | 10 |
3 | 1 | 20 |
4 | 1 | 10 |
5 | 2 | 10 |
-----------------------------------------------
now i need to SELECT table based on
now i want to merge all these three tables based on cont_selling table cont_selling_id column
and want to SUM cont_sold table price column and payment table paid column
this is what i want to do
expecting output
---------------------------------------------
cont_selling_id | price | paid |
---------------------------------------------
1 | 50 | 40 |
2 | 30 | 20 |
---------------------------------------------
so i tried like this in mysql query but it give wrong sum result
SELECT
SUM(Z.price) as total,
SUM(P.amount) as paid
FROM cont_selling S
LEFT JOIN cont_sold Z
ON S.cont_selling_id = Z.cont_selling_id
LEFT JOIN payment P
ON S.cont_selling_id = P.cont_selling_id
GROUP BY S.cont_selling_id
for this above query i m getting output like this
---------------------------------------------
cont_selling_id | price | paid |
---------------------------------------------
1 | 150 | 40 |
2 | 60 | 120 |
---------------------------------------------
Here how you can do it using the aggegare part into inner queries and then join
select
cs.cont_selling_id,
price,
paid
from cont_selling cs
left join(
select sum(price) as price , cont_selling_id from cont_sold
group by cont_selling_id
)x on x.cont_selling_id = cs.cont_selling_id,
left join(
select sum(paid) as paid , cont_selling_id from payment
group by cont_selling_id
)y
on y.cont_selling_id = cs.cont_selling_id;
You should make two different queries with SUM and then combine them to get the desired result:
SELECT T1.cont_selling_id,T1.price,T2.paid
FROM
(SELECT c.cont_selling_id,SUM(cs.price) as price
FROM cont_selling c LEFT JOIN
cont_sold cs ON c.cont_selling_id=cs.cont_selling_id
GROUP BY c.cont_selling_id) as T1 JOIN
(SELECT c.cont_selling_id,SUM(p.paid) as paid
FROM cont_selling c LEFT JOIN
payment p ON p.cont_selling_id=c.cont_selling_id
GROUP BY c.cont_selling_id) as T2 ON T1.cont_selling_id=T2.cont_selling_id
Result:
cont_selling_id price paid
----------------------------
1 50 40
2 30 20
Sample result in SQL Fiddle.
This untested query should work:
with a as(select cont_selling_id , sum(price) as totalprice from cont_sold group by cont_selling_id),
with a as(select cont_selling_id , sum(paid) as totalpaid from payment group by cont_selling_id),
select c.cont_selling_id , totalprice, totalpaid from cont_selling c left join a.count_selling_id = c.count_selling_id
left join b.count_selling_id = c.count_selling_id
You have to create temporary tables, because there is no dependency between your table for price and paid.

Mind numbing SQL madness

This query runs on an invoices table to help me decide who I need to pay
Here's the base table:
The users table
+---------+--------+
| user_id | name |
+---------+--------+
| 1 | Peter |
| 2 | Lois |
| 3 | Stewie |
+---------+--------+
The invoices table:
+------------+---------+----------+--------+---------------+---------+
| invoice_id | user_id | currency | amount | description | is_paid |
+------------+---------+----------+--------+---------------+---------+
| 1 | 1 | usd | 140 | Cow hoof | 0 |
| 2 | 1 | usd | 45 | Cow tail | 0 |
| 3 | 1 | gbp | 1 | Cow nostril | 0 |
| 4 | 2 | gbp | 1500 | Cow nose hair | 0 |
| 5 | 2 | cad | 1 | eyelash | 1 |
+------------+---------+----------+--------+---------------+---------+
I want a resulting table that looks like this:
+---------+-------+----------+-------------+
| user_id | name | currency | SUM(amount) |
+---------+-------+----------+-------------+
| 1 | Peter | usd | 185 |
| 2 | Lois | gbp | 1500 |
+---------+-------+----------+-------------+
The conditions are:
Only consider invoices that have not been paid, so where is_paid = 0
Group them by user_id, by currency
If the SUM(amount) < $100 for the user_id, currency pair then don't bother showing the result, since we don't pay invoices that are less than $100 (or equivalent, based on a fixed exchange rate).
Here's what I've got so far (not working -- which I guess is because I'm filtering by a GROUP'ed parameter):
SELECT
users.user_id, users.name,
invoices.currency, SUM(invoices.amount)
FROM
mydb.users,
mydb.invoices
WHERE
users.user_id = invoices.user_id AND
invoices.is_paid != true AND
SUM(invoices.amount) >=
CASE
WHEN invoices.currency = 'usd' THEN 100
WHEN invoices.currency = 'gbp' THEN 155
WHEN invoices.currency = 'cad' THEN 117
END
GROUP BY
invoices.currency, users.user_id
ORDER BY
users.name, invoices.currency;
Help?
You can't use SUM in a WHERE. Use HAVING instead.
Use HAVING clause instead of SUM in WHERE condition
Try this:
SELECT u.user_id, u.name, i.currency, SUM(i.amount) invoiceAmount
FROM mydb.users u
INNER JOIN mydb.invoices i ON u.user_id = i.user_id
WHERE i.is_paid = 0
GROUP BY u.user_id, i.currency
HAVING SUM(i.amount) >= (CASE i.currency WHEN 'usd' THEN 100 WHEN 'gbp' THEN 155 WHEN 'cad' THEN 117 END)
ORDER BY u.name, i.currency;
Try something like this:
SELECT
user_id, name, currency, sum(amount) due
FROM
invoice i
JOIN users u ON i.user_id=u.user_id
WHERE
is_paid = 0 AND
GROUP BY user_id, currency
having due >= 100
do you store exchange rates? Multiply rates with amount to get actual amount with respect to base currency.
sum(amount*ex_rate) due

MySQL Query : I want to use aggregate function between two tables?

I have two tables
Customer table
+--------+---------+
| refno | deposit |
+--------+---------+
| 1/13 | -10 |
| 10/13 | 500 |
| 100/13 | 0 |
| 101/13 | 250 |
| 102/13 | 1000 |
+--------+---------+
Ledger Table
+--------+----------+------+----------+
| refno | quantity | rate | recieved |
+--------+----------+------+----------+
| 1/13 | 2 | 70 | 0 |
| 10/13 | 3 | 80 | 0 |
| 100/13 | 3 | 60 | 0 |
| 101/13 | 4 | 60 | 0 |
| 102/13 | 10 | 65 | 0 |
+--------+----------+------+----------+
I want to customer column(deposit) added in ledger column (total)
I don't want to create another table.
i want
refno | total = customer.deposit+(ledger.quantity*ledger.rate-ledger.received)
1/13 | -200
10/13 | 4210
100/13| 625
101/13| 280
102/13| 1000
Regards,
select c.refno, c.deposit + l.total
from customer c join ledger l on c.refno = l.refno
I think author means update other than select from directly, but not sure which table used as basic table. For example, if we want to add value in Customer to that in Ledger, it should look like this:
update Ledger l inner join Cusomter c on l.refno = c.refno set l.total = l.total + c.deposit.
If you have duplicate refno in Ledger, you can try this way :
select b.refno, b.total + IFNULL(a.deposit, 0) as total
from
(select refno, sum(total) as total
from Ledger
group by refno) b
inner join Customer a on a.refno = b.refno
SQLFiddle demo
Is this what you want?
select l.refno, l.total, c.deposit, l.total + coalesce(c.deposit, 0) as TotalWithDeposit
from ledger l left outer join
customer c
on l.refno = c.refno;
Your question mentions aggregate functions, but I don't see any aggregations going on.
EDIT:
select l.refno, l.total, c.deposit,
c.deposit + (l.quantity * l.rate - l.received) as TotalWithDeposit
from ledger l left outer join
customer c
on l.refno = c.refno;