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
Related
table-1 Bill
| bill_id| date | supplier_id |amount|
| 101 | 2019-03-16| 1570 |1000 |
| 102 | 2019-05-20| 1570 |2500 |
table-2 payment
| bill_id| date | paid|
| 101 | 2019-03-17 | 800 |
| 101 | 2019-05-20 | 150 |
Expected result
| supplier_id |bill_id |amount|Paid | Balance|
| 1570 | 101 |1000 | 950 | 50 |
| 1570 | 102 |2500 | 0 | 2500 |
how do i achieve this
I would pre-aggregate payment
SELECT p.bill_id
, SUM(p.amount) AS paid
FROM payment p
GROUP BY p.bill_id
Then use that query as an inline view ...
SELECT b.supplier_id
, b.bill_id
, b.amount
, IFNULL(q.paid,0) AS paid
, b.amount - IFNULL(q.paid.0) AS balance
FROM bill b
LEFT
JOIN ( SELECT p.bill_id
, SUM(p.amount) AS paid
FROM payment p
GROUP BY p.bill_id
) q
ON q.bill_id = b.bill_id
ORDER
BY b.supplier_id
, b.bill_id
Note that we aren't given any guarantee about uniqueness of bill_id in bill table. It looks like it might be the primary key, but that's just a guess. If that's not unique, then the results from this query are likely not going to be what we want.
Notice that we are using an outer join (the LEFT keyword) so the query will return rows for bill for which there are no matching rows in payment. The IFNULL wrapper is shorthand way of converting a NULL value (which we would get if there isn't a matching row in payment) into a zero.
I have a table with transactions between multiple parties and want to query it to create visualization and perform basic accounting.
Table is like this:
+----+-----------+--------+----------------------------+-------------+------------+-------+-------+
| id |transaction| amount | logged_at | buyer | seller | b_pos | s_pos |
+----+-----------+--------+----------------------------+-------------+------------+-------+-------+
| 1 | 1 | 125000 | 2017-05-28 21:54:53.069000 | store2 | store1 | 4 | 5 |
| 2 | 1 | 109375 | 2017-05-28 21:54:53.069000 | store3 | store2 | 3 | 4 |
| 3 | 1 | 75000 | 2017-05-28 21:54:53.069000 | store4 | store3 | 2 | 3 |
| 4 | 1 | 100000 | 2017-05-28 21:54:53.069000 | store5 | store4 | 1 | 2 |
| 5 | 2 | 50000 | 2017-05-28 21:55:53.069000 | store5 | store3 | 1 | 2 |
So b_pos and s_pos is a position of a store in chain of transactions between those. So chain looks like store1 -> store2 -> store3 and so on.
So I am trying to do several things with my SQL.
Create all distinct path between parties for visualization
Calculate total amount of goods being sold between parties in those paths
Total number of transactions was performed in these distinct paths.
Here's my SQL. The only problem is this query is getting pretty slow with table over 1mil records (30 sec) and my table could be as big as 700mil records. How should I approach this problem?
I can restrict queries to certain time intervals but it needs to be reasonably fast.
SELECT seller, CONCAT(seller, s_pos - offset) seller_id, buyer,
CONCAT(buyer, b_pos - offset) buyer_id, SUM(amount), cnt as transactions, amount/cnt as ROI
FROM transaction_table
JOIN (SELECT DISTINCT transaction,
CASE
WHEN seller = 'store3' THEN s_pos
WHEN buyer = 'store3' THEN b_pos
END AS offset
FROM
transaction_table
WHERE buyer = 'store3' OR seller = 'store3'
AND logged_at >= '2014-06-23 17:34:20'
AND logged_at <= '2018-06-23 17:34:00'
) ck_offset
ON transaction_table.transaction = ck_offset.transaction
JOIN
(SELECT transaction, count(transaction) as cnt from (select * from transaction_table
WHERE buyer = 'store3' OR seller = 'store3'
AND logged_at >= '2014-06-23 17:34:20'
AND logged_at <= '2018-06-23 17:34:00' group by transaction, logged_at) AS dist_chainkeys
group BY transaction) key_counts
ON key_counts.transaction = ck_offset.transaction
WHERE logged_at >= '2014-06-23 17:34:20'
AND logged_at <= '2018-06-23 17:34:00'
GROUP BY seller, seller_id, buyer, buyer_id;
Do your tables use indexes? Indexes are the way to boost the performance. Have a look to this:
What is an index in SQL?
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
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.
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