How to select only first matching value from another table in MySQL? - mysql

Here is my database schema:
Payment table:
+------------+--------+--------+---------------------+
| payment_id | tab_id | amount | created |
+------------+--------+--------+---------------------+
| 1 | 1 | 5 | 2017-05-22 12:14:27 |
| 2 | 2 | 10 | 2017-05-22 12:15:21 |
| 3 | 2 | 1 | 2017-05-22 13:11:14 |
+------------+--------+--------+---------------------+
Tab table:
+------------+----------------+
| tab_id | service_charge |
+------------+----------------+
| 1 | 1 |
| 2 | 3 |
+------------+----------------+
I need to calculate total amounts (amount + service_charge) per payment, but service_charge should be included only in first payment matching tab_id.
My current query:
SELECT
payment.payment_id,
(payment.amount + tab.service_charge) as total_amount,
payment.created
FROM payment
INNER JOIN tab
ON payment.tab_id = tab.tab_id;
Actual result:
As you can see below service_charge from tab_id = 2 included twice (payment_id = 2 and payment_id = 3).
+------------+-----------------+---------------------+
| payment_id | total_amount | created |
+------------+-----------------+---------------------+
| 1 | 6 | 2017-05-22 12:14:27 |
| 2 | 13 | 2017-05-22 12:15:21 |
| 3 | 4 | 2017-05-22 13:11:14 |
+------------+-----------------+---------------------+
Expected result:
total_amount should not include service_charge in payment_id = 3 as shown below.
+------------+-----------------+---------------------+
| payment_id | total_amount | created |
+------------+-----------------+---------------------+
| 1 | 6 | 2017-05-22 12:14:27 |
| 2 | 13 | 2017-05-22 12:15:21 |
| 3 | 1 | 2017-05-22 13:11:14 |
+------------+-----------------+---------------------+

You should determine which is the first payment matching the tab_id and then based on that info, decide if you want to use the service_charge or not:
SELECT
payment.payment_id,
payment.amount + if (payment.created=m.mintime, tab.service_charge, 0) as total_amount,
payment.created
FROM payment
INNER JOIN tab
ON payment.tab_id = tab.tab_id
JOIN (
SELECT tab_id, min(created) as 'mintime'
FROM payment
GROUP BY tab_id
) AS m on m.tab_id = payment.tab_id;

Related

How to get data status wise after grouping?

I want to get count of different statuses for bookings for each event and some other data. Each row should represent an event.
So I have an events table and a bookings table.
Events table has id, name, max_allowed
Bookings table has id,event_id,status
Status can be booked, canceled, waitlisted.
I want to get data for all events with the count for each status.
So I need these columns -
event_id
booked_count
canceled_count
waitlisted_count
remaining_slots - (max_allowed - booked_count)
occupancy_rate - booked_count/max_allowed
Sample data:
Events
| id | name | max_allowed |
|---- |--------- |------------- |
| 1 | Yoga | 5 |
| 2 | Boxing | 2 |
| 3 | Pilates | 5 |
Bookings
| id | event_id | status |
|---- |---------- |------------ |
| 1 | 1 | booked |
| 2 | 1 | booked |
| 3 | 2 | booked |
| 4 | 2 | canceled |
| 5 | 2 | booked |
| 6 | 2 | waitlisted |
| 7 | 3 | booked |
| 8 | 3 | booked |
| 9 | 3 | booked |
Output:
| event_id | booked_count | canceled_count | waitlisted_count | remaining_slots | occupancy_rate |
|---------- |-------------- |---------------- |------------------ |----------------- |---------------- |
| 1 | 2 | 0 | 0 | 3 | 0.4 |
| 2 | 2 | 1 | 1 | 0 | 1 |
| 3 | 3 | 0 | 0 | 2 | 0.6 |
Use conditional aggregation:
select t.*,
greatest(0, t.max_allowed - t.booked_count + t.canceled_count - t.waitlisted_count) remaining_slots,
least(t.max_allowed, t.booked_count - t.canceled_count + t.waitlisted_count) / t.max_allowed occupancy_rate
from (
select e.id, e.name, e.max_allowed,
sum(status = 'booked') booked_count,
sum(status = 'canceled') canceled_count,
sum(status = 'waitlisted') waitlisted_count
from Events e left join Bookings b
on b.event_id = e.id
group by e.id, e.name, e.max_allowed
) t
Try below query.
with src_data as
(select
event_id,
sum(case when status='booked' then 1 else 0 end ) as booked_count,
sum(case when status='canceled' then 1 else 0 end ) as canceled_count,
sum(case when status='waitlisted' then 1 else 0 end ) as waitlisted_count
from bookings group by event_id
)
select
s.event_id,
s.booked_count,
s.canceled_count,
s.waitlisted_count,
e.max_allowed-s.booked_count,
s.booked_count/e.max_allowed
from events e inner join src_data s
on e.id=s.event_id;

Combine multiple select statement in one result table

I have two tables, one for sales and another for stock.
I want to select location id, item id, size id and sales qty from sales table, while I want just to select stock qty from stock table for the same location id and size id from sales table, like this:
Sales table:
------------------------------------
| loc_id | item_id | size_id | qty |
------------------------------------
| 5 | 11321 | 1 | 5 |
| 5 | 11321 | 2 | 8 |
| 5 | 11321 | 3 | 4 |
| 5 | 11321 | 2 | 1 |
Stock table:
------------------------------------
| loc_id | item_id | size_id | qty |
------------------------------------
| 5 | 11321 | 1 | 3 |
| 5 | 11321 | 2 | 7 |
| 5 | 11321 | 3 | 9 |
So the result after select should be like this:
------------------------------------------------------
| loc_id | item_id | size_id | sales_qty | stock_qty |
------------------------------------------------------
| 5 | 11321 | 1 | 5 | 3 |
| 5 | 11321 | 2 | 9 | 7 |
| 5 | 11321 | 3 | 4 | 9 |
Here's what I tried to do:
SELECT SUM(T1.qty) AS `salesQty`, SUM(T2.qty) AS `stockQty`, T1.size_id,
T1.loc_id
FROM sales T1
INNER JOIN stock T2 ON T2.item_id = T1.item_id AND T2.size_id = T1.size_id
WHERE T1.item_id = '11321'
AND T1.size_id IN (1,2,3)
AND T1.loc_id IN (5)
GROUP BY T1.size_id, T1.loc_id
But stock qty always wrong!
select
q1.loc_id
,q1.item_id
,q1.size_id
,sum(case when q1.Type='Sales' then q1.Qty else 0 end) as sales_qty
,sum(case when q1.Type='Stock' then q1.Qty else 0 end) as stock_qty
from (
select
T1.loc_id
,T1.item_id
,T1.size_id
,'Sales' as Type
,SUM(T1.qty) AS Qty
from sales T1
group by
T1.loc_id
,T1.item_id
,T1.size_id
union all
select
T2.loc_id
,T2.item_id
,T2.size_id
,'Stock' as Type
,SUM(T2.qty) AS Qty
from stock T2
group by
T2.loc_id
,T2.item_id
,T2.size_id) q1
group by
q1.loc_id
,q1.item_id
,q1.size_id

Updating a column after deleting a row

I'm learning to use MySQL and therefore I'm trying to make a simple inventory management.
If I delete an invoice how can I not just delete from the invoice table and from the invoiceItemTable as well but update the "Amount" column with the subtraction value at the right store in my Inventory Table? Since if I want to do
SUM(inventory.amount) - SUM(SELECT amount FROM InvoiceItemTable WHERE invoice_id = 1
GROUP BY Product_id)
this will fail because subqueries have more than 1 rows. Below you can see my database structure.
InvoceTable
+---+----------------+----------+
|ID | invoice_number | Store_id |
+---+----------------+----------+
| 1 | 1234 | 1 |
+---+----------------+----------+
InvoiceItemTable
+---+----------------+------------+---------+
|ID | invoice_id | Product_id | Amount |
+---+----------------+------------+---------+
| 1 | 1 | 1 | 5 |
+---+----------------+------------+---------+
| 2 | 1 | 1 | 5 |
+---+----------------+------------+---------+
| 3 | 2 | 1 | 10 |
+---+----------------+------------+---------+
Inventory
+---+----------------+---------+----------+
|ID | Product_id | Amount | Store_id |
+---+----------------+---------+----------+
| 1 | 1 | 15 | 1 |
+---+----------------+---------+----------+
| 2 | 2 | 15 | 1 |
+---+----------------+---------+----------+
| 3 | 2 | 15 | 2 |
+---+----------------+---------+----------+
UPDATE:
Expectation after the queries:
InvoceTable
+---+----------------+----------+
|ID | invoice_number | Store_id |
+---+----------------+----------+
| | | |
+---+----------------+----------+
InvoiceItemTable
+---+----------------+------------+---------+
|ID | invoice_id | Product_id | Amount |
+---+----------------+------------+---------+
| 3 | 2 | 1 | 10 |
+---+----------------+------------+---------+
Inventory
+---+----------------+---------+----------+
|ID | Product_id | Amount | Store_id |
+---+----------------+---------+----------+
| 1 | 1 | 5 | 1 |
+---+----------------+---------+----------+
| 2 | 2 | 5 | 1 |
+---+----------------+---------+----------+
| 3 | 2 | 15 | 2 |
+---+----------------+---------+----------+
you'd need to use sum inside the subquery:
select SUM(inventory.amount) - (SELECT SUM(amount) FROM InvoiceItemTable WHERE invoice_id = 1 GROUP BY Product_id)
from inventory
check sqlfiddle for your database scheme and the running query.
this query should decrease the Inventory table's amount column according to invoiceitemtable table:
update Inventory set Amount = Amount - (SELECT SUM(amount) FROM InvoiceItemTable WHERE invoice_id = 1 AND Product_id = Inventory.Product_id GROUP BY Product_id)
SUM(inventory.amount) - (SELECT SUM(amount) FROM InvoiceItemTable WHERE invoice_id = 1 GROUP BY Product_id)

Select total members and amount paid

I need help generating SQL for MySQL database.
I have three tables:
Organisations
Members
Payments
Organisations table:
+------------+---------+--------+
| id | name |website |
+------------+---------+--------+
| 1 | AAA | a.com |
|-------------------------------+
| 2 | BBB | b.com |
+------------+---------+--------+
Members table:
+------------+-------------------+--------+-----------------+-----------+
| id | organisation_id |name | Payment_confirm | join_date |
+------------+-------------------+--------+-----------------+-----------+
| 1 | 1 | james | 1 | 2013-8-02 |
|-----------------------------------------+-----------------+-----------+
| 2 | 1 | Jimmy | 0 | 2013-6-25 |
+------------+-------------------+--------+-----------------+-----------+
| 3 | 2 | Manny | 1 | 2013-07-02|
|-----------------------------------------+-----------------+-----------+
| 4 | 1 | Kim | 1 | 2013-09-02|
+------------+-------------------+--------+-----------------+-----------+
Payments table:
+------------+-------------------+--------+-----------------+----------------+
| id | member_id |amount | transaction_id | transferred_at |
+------------+-------------------+--------+-----------------+----------------+
| 1 | 1 | 100 | T1001 | 2013-8-03 |
|-----------------------------------------+-----------------+--------------- +
| 2 | 2 | 0 | null | Null |
+------------+-------------------+--------+-----------------+----------------+
| 3 | 3 | 200 | T1002 | Null |
|-----------------------------------------+-----------------+----------------+
| 4 | 4 | 50 | T1005 | 2013-09-05 |
+------------+-------------------+--------+-----------------+----------------+
How can I select the following?
Expecting the following output:
+------------+-------------------+--------+-----------------+---------------+--------------+
| Org name | Revenue |untransferred amount | Total members | last 30 days |
+------------+-------------------+--------------------------+---------------+--------------+
| AAA | 150 | 0 | 3 | 2 |
|-----------------------------------------------------------+---------------+--------------+
| BBB | 200 | 200 | 1 | 0 |
+------------+-------------------+--------------------------+---------------+--------------+
Org name = organisation name
Revenue = Total amount received
untransferred amount = transferred_at is null (payments table)
Total members = total members joined till today
last 30 days = total members joined last 30 days
You need to join your tables, group the results and select the desired logic:
SELECT org.name,
SUM(pmt.amount) AS revenue,
SUM(IF(pmt.transferred_at IS NULL, pmt.amount, 0)) AS untransferred
FROM Organisations org
JOIN Members mem ON mem.organisation_id = org.id
JOIN Payments pmt ON pmt.member_id = mem.id
GROUP BY org.id
See it on sqlfiddle.
select o.name,
sum(amount) as Revenue,
sum(if(transferred_at is null, amount, 0)) as untransfered_ammt,
sum(if(join_date>=curdate() - interval 30 day, 1, 0)) as last_30_d
from organisations o
inner join members m on o.id=m.organisation_id
inner join payments p on p.member_id=m.member_id
group by 1

MySQL Select one row with unique attribute value

I'm building a stock keeping system and decided to store each product's balance (everytime it's updated) into the following table:
+------------+--------------+---------+------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+------+
Example:
Staff adds 10 pieces to product_id 123 in warehouse_id 5
+------------+--------------+---------+-------------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+-------------+
| 123 | 5 | 10 | 2013-09-16 |
+------------+--------------+---------+-------------+
Staff then adds 3 pieces to product 234 in warehouse_id 5, and
5 pieces to 123 in warehouse_id 5,
+------------+--------------+---------+-------------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+-------------+
| 123 | 5 | 10 | 2013-09-16 |
| 234 | 5 | 3 | 2013-09-18 |
| 123 | 5 | 15 | 2013-09-21 |
+------------+--------------+---------+-------------+
*Notice the date column
Now let me add a few more rows
+------------+--------------+---------+-------------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+-------------+
| 123 | 5 | 10 | 2013-09-16 |
| 234 | 5 | 3 | 2013-09-18 |
| 123 | 5 | 15 | 2013-09-21 |
| 123 | 5 | 18 | 2013-09-24 |
| 234 | 5 | 10 | 2013-09-26 |
| 123 | 5 | 22 | 2013-09-29 |
+------------+--------------+---------+-------------+
How do i run a query that would get me all products' balances as at 25th of September 2013?
That means i need the following result:
+------------+--------------+---------+-------------+
| Product_id | Warehouse_id | Balance | Date |
+------------+--------------+---------+-------------+
| 234 | 5 | 3 | 2013-09-18 |
| 123 | 5 | 18 | 2013-09-24 |
+------------+--------------+---------+-------------+
In short I need the latest row (by date), per product_id.
Any help would be greatly appreciated!
Assuming that products' balances are being maintained per warehouse you can do it like this
SELECT t.product_id, t.warehouse_id, t.balance, t.date
FROM table1 t JOIN
(
SELECT warehouse_id, product_id, MAX(date) date
FROM table1
WHERE date <= '2013-09-25'
GROUP BY warehouse_id, product_id
) q
ON t.warehouse_id = q.warehouse_id
AND t.product_id = q.product_id
AND t.date = q.date
Output:
| PRODUCT_ID | WAREHOUSE_ID | BALANCE | DATE |
|------------|--------------|---------|------------|
| 234 | 5 | 3 | 2013-09-18 |
| 123 | 5 | 18 | 2013-09-24 |
Here is SQLFiddle demo
SELECT *
FROM TABLE
WHERE (PRODUCT_ID, DATE) IN
(SELECT PRODUCT_ID, MAX(DATE) FROM TABLE
WHERE DATE <= '2013-09-25'
GROUP BY PRODUCT_ID )
Query:
SQLFIDDLEExample
SELECT *
FROM table1 t
WHERE t.`Date` = (SELECT MAX(t2.`Date`)
FROM Table1 t2
WHERE t2.`Date` <= '2013-09-25'
AND t2.product_id = t.product_id)