Mind numbing SQL madness - mysql

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

Related

select data from multiple tables in one mysql query

I have three tables
1 Policies
| date | policy_No | client_no | premium | policy_type |
| 2019-01-23 | 10002 | 1570 | 4000 | New policy |
| 2019-03-15 | 10003 | 1570 | 16000 | Renewal policy|
2 Endorsements
|date |client_no | policy_no| premium| endorsement_type|
|2019-02-17 | 1570 | 10002 | 2000 | Debit
|2019-03-17 | 1570 | 10003 | -4000 | Credit
3 Payment
| date | client_id | policy_no| amount|
| 2019-03-16| 1570 | 10003 | 10000 |
expected result
| date | type | amount|
| 2019-01-23 | New Policy | 4000 |
| 2019-02-17 | Debit endorsement | 2000 |
| 2019-03-15 | Renewal policy | 16000 |
| 2019-03-16 | Payment | 10000 |
| 2019-03-17 | Credit endorsement| -4000 |
how do i achieve this in one MySQL query
We can try using a union query here:
SELECT date, policy_type AS type, premium AS amount FROM Policies
UNION ALL
SELECT
date,
CASE WHEN endorsement_type = 'Debit' THEN 'Debit endorsement'
WHEN endorsement_type = 'Credit' THEN 'Credit endorsement' END,
premium
FROM Endorsements
UNION ALL
SELECT date, 'Payment', amount FROM Payment
ORDER BY date;
your tables have 2 common fields and it's not easy to determine which is serving as the primary key or foreign key. However, to select fields from different tables, You can use LEFT JOIN RIGHT JOIN INNER JOIN or FULL OUTER JOIN.
You should read this post from W3schools to decide which is the best option for you.
The typical syntax is like
SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders
INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;
Hope, all three tables have the foreign key. So, we can able to use JOIN concept also like below code,
SELECT * from Policies PS
INNER JOIN Endorsements E ON E.policy_no = PS.policy_no
INNER JOIN Payment P ON P.client_id = PS.client_no
ORDER BY date ASC
Thanks,
Try that,
SELECT date, policy_type type, premium amount
FROM Policies
UNION ALL
SELECT date,
CASE WHEN endorsement_type = 'Debit' THEN 'Debit endorsement'
WHEN endorsement_type = 'Credit' THEN 'Credit endorsement'
END type,
premium amount
FROM Endorsements
UNION ALL
SELECT date, 'Payment', amount FROM Payment

Selecting related rows in MySQL

Let me elaborate. I have a table like this (updated to include more example)
| id | date | cust | label | paid | due |
+----+-----------+------+-------------------------+------+-------+
| 1 |2016-02-02 | 1 | SALE: Acme Golf Balls | 0 | 1000 |
| 20 |2016-03-01 | 1 | PAYMENT: transaction #1 | 700 | 0 |
| 29 |2016-03-02 | 1 | PAYMENT: transaction #1 | 300 | 0 |
| 30 |2016-03-02 | 3 | SALE: Acme Large Anvil | 500 | 700 |
| 32 |2016-03-02 | 3 | PAYMENT: transaction #30| 100 | 0 |
| 33 |2016-03-03 | 2 | SALE: Acme Rockets | 0 | 2000 |
Now I need to output a table that displays sales that haven't been paid in full and the remaining amount. How do I do that? There's not much info out there on how to relate rows from the same table.
EDIT: Here's the output table I'm thinking of making
Table: debts_n_loans
| cust | label | amount |
==========================================
| 3 | SALE: Acme Large Anvil | 100 |
| 2 | SALE: Acme Rockets | 2000 |
If cust is the key that ties them together, then you can just use aggregation and a having clause:
select cust, sum(paid), sum(due)
from t
group by cust
having sum(paid) <> sum(due);
If you want the details, you can use a join, in or exists to get the details.
EDIT:
If you need to do this using the transaction at the end of the string:
select t.id, t.due, sum(tpay.paid) as paid
from t left join
t tpay
on tpay.label like '%#' || t.id
where t.label like 'SALE:%' and
tpay.label like 'PAYMENT:%'
group by t.id, t.due
having t.due <> sum(tpay.paid);
So you only need the rows with a due greater than 0
SELECT * FROM <table> WHERE due > 0;
Try this:
SELECT
cust,
SUM(due) - SUM(paid) AS remaining
FROM t1
GROUP BY cust
HAVING SUM(due) > SUM(paid);

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;

Get the balance of my users in the same table

Help please, I have a table like this:
| ID | userId | amount | type |
-------------------------------------
| 1 | 10 | 10 | expense |
| 2 | 10 | 22 | income |
| 3 | 3 | 25 | expense |
| 4 | 3 | 40 | expense |
| 5 | 3 | 63 | income |
I'm looking for a way to use one query and retrive the balance of each user.
The hard part comes when the amounts has to be added on expenses and substracted on incomes.
This would be the result table:
| userId | balance |
--------------------
| 10 | 12 |
| 3 | -2 |
You need to get each totals of income and expense using subquery then later on join them so you can subtract expense from income
SELECT a.UserID,
(b.totalIncome - a.totalExpense) `balance`
FROM
(
SELECT userID, SUM(amount) totalExpense
FROM myTable
WHERE type = 'expense'
GROUP BY userID
) a INNER JOIN
(
SELECT userID, SUM(amount) totalIncome
FROM myTable
WHERE type = 'income'
GROUP BY userID
) b on a.userID = b.userid
SQLFiddle Demo
This is easiest to do with a single group by:
select user_id,
sum(case when type = 'income' then amount else - amount end) as balance
from t
group by user_id
You could have 2 sub-queries, each grouped by id: one sums the incomes, the other the expenses. Then you could join these together, so that each row had an id, the sum of the expenses and the sum of the income(s), from which you can easily compute the balance.

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

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