mysql, subquery or join in group by condition - mysql

+------------+------+-----------------------+
| invoice | amount | paid | date |
+------------+--------+--------+------------+
| 1001 | 55 | 1 | 2015-01-01 |
+------------+--------+--------+------------+
| 1002 | 30 | 0 | 2015-02-01 |
+------------+--------+--------+------------+
| 1003 | 59 | 1 | 2015-02-01 |
+------------+--------+--------+------------+
| 1004 | 78 | 0 | 2015-03-01 |
+------------+--------+--------+------------+
| 1005 | 65 | 1 | 2015-03-01 |
+------------+--------+--------+------------+
| 1006 | 107 | 0 | 2015-04-01 |
+------------+--------+--------+------------+
For example, if I have above table,
I want to get total invoice amount and paid amount.
for total invoice amount
select sum(amount) from invoice group by DATE_FORMAT(date,"%Y-%m")
to get paid invoice
select sum(amount) from invoice where paid=1 group by DATE_FORMAT(date,"%Y-%m")
How can I combine it into single query?

Use IF to sum just the rows with paid = 1.
SELECT DATE_FORMAT(date, '%Y-%m') AS month,
SUM(amount) AS total,
SUM(IF(paid = 1, amount, 0)) AS total_paid
FROM invoice
GROUP BY month;

Related

How to get Total Overdraft amounts from a particular Date in SQL

I'm trying to get the total amount of overdraft accounts from an old Date, the goal is to get the total amount it was on the 31st of January.
I have the following tables Users and Transactions.
USERS (currently)
| user_id | name | account_balance |
|---------|---------|------------------|
| 1 | Wells | 1.00 |
| 2 | John | -10.00 |
| 3 | Sahar | -5.00 |
| 4 | Peter | 1.00 |
TRANSACTIONS (daily transition can go back in time)
| trans_id | user_id | amount_tendered | trans_datetime |
|------------|---------|-------------------|---------------------|
| 1 | 1 | 2 | 2021-02-16 |
| 2 | 2 | 3 | 2021-02-16 |
| 3 | 3 | 5 | 2021-02-16 |
| 4 | 4 | 2 | 2021-02-16 |
| 5 | 1 | 10 | 2021-02-15 |
so the current total overdraft amount is
SELECT sum(account_balance) AS O_D_Amount
FROM users
WHERE account_balance < 0;
| O_D_Amount |
|------------|
| -15 |
I need Help to reverse this amount to a date in history.
Assuming overdrafts are based on the sum of transactions up to a point, you can use a subquery:
select sum(total) as total_overdraft
from (select user_id, sum(amount_tendered) as total
from transactions t
where t.trans_datetime <= ?
group by user_id
) t
where total < 0;
The ? is a parameter placeholder for the date/time you care about.

SQL query to get Net Salse by every month

I'm looking for a query to get monthly net sales I tried this far but I couldn't get what I want.
this is my Order Table
+----------+-----------+--------+------------+---------------+-------------+-----------+---------+---------+
| orderID | custID | userID | orderDate | paymentMethod | grossAmount | netAmount | cash | balance |
+----------+-----------+--------+------------+---------------+-------------+-----------+---------+---------+
| INV-0001 | CUST-0001 | U-001 | 2020-05-01 | Cash Pay | 525.00 | 525.00 | 550.00 | 25.00 |
| INV-0002 | CUST-0001 | U-001 | 2020-05-01 | Cash Pay | 240.00 | 240.00 | 250.00 | 10.00 |
| INV-0003 | CUST-0001 | U-001 | 2020-05-01 | Cash Pay | 220.00 | 220.00 | 250.00 | 30.00 |
| INV-0004 | CUST-0001 | U-001 | 2020-04-30 | Cash Pay | 895.00 | 895.00 | 1000.00 | 105.00 |
| INV-0005 | CUST-0001 | U-001 | 2020-04-30 | Cash Pay | 300.00 | 300.00 | 500.00 | 200.00 |
| INV-0006 | CUST-0001 | U-001 | 2020-04-30 | Cash Pay | 230.00 | 230.00 | 250.00 | 20.00 |
+----------+-----------+--------+------------+---------------+-------------+-----------+---------+---------+
This is my CustomerReturn Table
+-------+----------+------------+--------+------------+-----------+-----------+-------------+
| retID | orderID | itemCode | userID | retDate | returnQty | unitPrice | totalAmount |
+-------+----------+------------+--------+------------+-----------+-----------+-------------+
| 1 | INV-0001 | 1800232050 | U-001 | 2020-05-01 | 1.00 | 100.00 | 100.00 |
| 2 | INV-0002 | 1909873674 | U-001 | 2020-05-01 | 2.00 | 55.00 | 110.00 |
| 3 | INV-0004 | 1800232050 | U-001 | 2020-04-30 | 1.00 | 100.00 | 100.00 |
+-------+----------+------------+--------+------------+-----------+-----------+-------------+
the formula is (total of the monthly bill(Order.netAmount) - a total of monthly return (CustomerReturn.totalAmount))
in need to get net sales every year of every month.
select orderDate,sum(netAmount)-sum(totalAmount) from `Order` o,CustomerReturn r where o.orderID=r.orderID GROUP BY orderDate;
when I run this query it shows me this
+------------+---------------------------------+
| orderDate | sum(netAmount)-sum(totalAmount) |
+------------+---------------------------------+
| 2020-04-30 | 795.00 |
| 2020-05-01 | 555.00 |
+------------+---------------------------------+
but it should be Like this
+------------+---------------------------------+
| orderDate | sum(netAmount)-sum(totalAmount) |
+------------+---------------------------------+
| 2020-04-30 | 1425.00 |
| 2020-05-01 | 775.00 |
+------------+---------------------------------+
please help me. Thank you.!
Your query is good, it is fetching all records when there is a match on OrderId in the table CustomerReturn and doing the sums as you requested, however there are no returns for the order INV-0003, so this condition o.orderID=r.orderID is not valid when it comes to that record and it is ignoring that data. Doing a left join will fix the issue.
select
o.orderDate,
sum(o.netAmount)-sum(case when cr.totalAmount is null then 0 else cr.totalAmount end)
from
Orders o
left join
CustomerReturn cr
on
o.orderID = cr.orderID
group by
o.orderDate
A left join will cause cr.totalAmount to have null values in case there is no match for o.orderID=r.orderID then we use this part; case when cr.totalAmount is null then 0 else cr.totalAmount end to fix that null issue.
Because you are joining on dates that is why you are not getting correct answer, as order date and return date can have different month.
Better if you extract the month and then do sum as shown in below query, and here is the demo.
select
o.mm as month,
sum(total_net_amount - total_amount) as total
from
(
select
month(orderDate) as mm,
sum(netAmount) as total_net_amount
from Orders
group by
month(orderDate)
) o
join
(
select
month(retDate) as mm,
sum(totalAmount) as total_amount
from CustomerReturn
group by
month(retDate)
) cr
on o.mm = cr.mm
group by
o.mm
Output:
*--------------*
|month | total |
*--------------*
| 5 | 775 |
| 4 | 1325 |
*--------------*
Learn to use proper, explicit, standard, readable JOIN syntax. As pointed out in another answer, you want a LEFT JOIN. That said, the simpler way to write the logic is:
select o.orderDate,
sum(o.netAmount)- coalesce(sum(cr.totalAmount, 0)) as net_amount
from Orders o left join
CustomerReturn cr
on o.orderID = cr.orderID
group by o.orderDate;

MySQL Select SUM of Total Transaction

I have two tables transaction and detailtransaction, the relation is 1 to N
, here's transaction Table
+-----+----------+--------+---------------------+------------+-------------+---------+---
| ID | TransactionDate | GrandTotal | DownPayment | Status | DatePaid |
+-----+----------+--------+---------------------+------------+-------------+---------+---
| 173 | 2018-01-03 22:05:00 | 26000.00 | 26000.00 | PAID OFF | 2018-01-03 21:05:52 |
| 174 | 2018-01-01 22:06:00 | 26000.00 | 0.00 | PAID OFF | 2018-01-03 22:05:52 |
| 175 | 2018-01-02 22:06:00 | 60000.00 | 10000.00 | - 50000 | 2018-01-03 21:06:55 |
| 176 | 2018-01-03 22:08:00 | 90000.00 | 50000.00 | - 40000 | 2018-01-03 21:08:19 |
| 178 | 2018-01-03 22:34:00 | 70000.00 | 70000.00 | PAID OFF | 2018-01-03 21:35:00 |
| 179 | 2018-01-03 23:13:00 | 52000.00 | 52000.00 | PAID OFF | 2018-01-03 22:13:35 |
+-----+----------+--------+---------------------+------------+-------------+---------+---
and here's transactiondetail table
+----+---------------+-----------+---------------+--------+------+----------+----------+------------+
| ID | TransactionID | ProductID | ServiceID | UserID | Tax | Discount | Quantity | PriceTotal |
+----+---------------+-----------+---------------+--------+------+----------+----------+------------+
| 1 | 173 | NULL | SV031 | 7 | NULL | 0 | 0 | 26000.00 |
| 2 | 174 | NULL | SV032 | 7 | NULL | 0 | 0 | 26000.00 |
| 3 | 175 | NULL | SV033 | 7 | NULL | 0 | 0 | 60000.00 |
| 4 | 176 | 8 | NULL | 7 | NULL | 0 | 1 | 30000.00 |
| 5 | 176 | NULL | SV034 | 7 | NULL | 0 | 0 | 60000.00 |
| 7 | 178 | 5 | NULL | 7 | NULL | 0 | 1 | 70000.00 |
| 8 | 179 | NULL | SV036 | 7 | NULL | 0 | 0 | 26000.00 |
| 9 | 179 | NULL | SV037 | 7 | NULL | 0 | 0 | 26000.00 |
+----+---------------+-----------+---------------+--------+------+----------+----------+------------+
There's 2 things can be made in 1 transaction, buy a product or a service. So there's a ServiceID and ProductID on TransactionDetail's.
on ID 173, someone made transaction of service and paid off
ID 174, someone made transaction 3 days ago and paid it today
ID 175 transaction of service and put Downpayment of 10k
ID 176 transaction of 1 product cost 30k and service cost 60k, downpayment is 50k (minus 40k)
ID 178 buying some product cost 70k
ID 179 2 service and paid off the transaction
The rule is product cant be paid later, only service can
I want to make a report of daily income, how to sum is based on the DatePaid, the result i should get is 26k + 26k + 10k + 50k + 70K + 52k = 230K
i've tried to get SUM by joining the table what i got is it repeating the value, like if theres 2 service in 1 transaction, it sums like its 2 transaction. here's what i tried, i got 336k
SELECT SUM(CASE WHEN Status = 'PAID OFF' THEN GrandTotal ELSE DownPayment END) FROM `transaction` a LEFT JOIN transactiondetail b ON a.ID = b.TransactionID WHERE DatePaid BETWEEN '2018-01-03 00:00:00' AND '2018-01-03 23:59:59'
Also how can I sum only transactiondetail with serviceID so that the product isnt the sum.
Thanks
First of all, if all you need is just the sum of amount, you don't need to join with transaction details. Just the below query will do.
SELECT SUM(CASE WHEN Status = 'PAID OFF' THEN GrandTotal ELSE DownPayment END) FROM transaction WHERE DatePaid BETWEEN '2018-01-03 00:00:00' AND '2018-01-03 23:59:59'
Secondly, what details do you need from transaction details table, do you want to see the amount by product and service grouped.
To get the total sum :
SELECT SUM(CASE WHEN Status = 'PAID OFF' THEN GrandTotal ELSE DownPayment END) FROM `transaction` WHERE DatePaid BETWEEN '2018-01-03 00:00:00' AND '2018-01-03 23:59:59'
removing products can by done by checking if product_id is not null and to get total use sub query as:
SELECT SUM(total) FROM( SELECT SUM(CASE WHEN Status = 'PAID OFF' THEN GrandTotal ELSE DownPayment END) AS total FROM `transaction` a LEFT JOIN transactiondetail b ON a.ID = b.TransactionID WHERE DatePaid BETWEEN '2018-01-03 00:00:00' AND '2018-01-03 23:59:59' AND ProductID IS NULL GROUP BY a.ID ) as t

Fetching Total Sale of each day and month from database

I have table with column c_date as datetime, total as int type in mysql, and i want to print out sale of each day, and total sale of each month, and total sale annually including day, month, year where there was no sale.
Currently for daily sale, I am running below query :
mysql> select date(c_date) as date, sum(total) as total_sale from sale group by date;
+------------+------------+
| date | total_sale |
+------------+------------+
| 2013-10-3 | 798 |
| 2013-10-6 | 114 |
+------------+------------+
but, i want something like this :
mysql> select date(c_date) as date, sum(total) as total_sale from sale group by date;
+------------+------------+
| date | total_sale |
+------------+------------+
| 2013-10-1 | 0 |
| 2013-10-2 | 0 |
| 2013-10-3 | 798 |
| 2013-10-4 | 0 |
| 2013-10-5 | 0 |
| 2013-10-6 | 114 |
+------------+------------+
and for Monthly, I am getting this :
mysql> select c_date, month(c_date) as month, year(c_date) as year, sum(total) as total from sale group by c_date order by c_date;
+---------------------+-------+------+-------+
| c_date | month | year | total |
+---------------------+-------+------+-------+
| 2013-10-3 02:40:06 | 10 | 2013 | 228 |
| 2013-10-3 02:41:58 | 10 | 2013 | 114 |
| 2013-10-3 02:44:36 | 10 | 2013 | 114 |
| 2013-10-3 02:46:40 | 10 | 2013 | 114 |
| 2013-10-3 02:49:15 | 10 | 2013 | 114 |
| 2013-10-3 02:53:36 | 10 | 2013 | 114 |
| 2013-10-6 07:43:27 | 10 | 2013 | 114 |
+---------------------+-------+------+-------+
But i want something like this :
mysql> select c_date, month(c_date) as month, year(c_date) as year, sum(total) as total from sale group by c_date order by c_date;
+---------------------+-------+------+-------+
| c_date | month | year | total |
+---------------------+-------+------+-------+
| 2013-1-3 02:40:06 | 1 | 2013 | 0 |
| 2013-2-3 02:41:58 | 2 | 2013 | 0 |
| 2013-3-3 02:44:36 | 3 | 2013 | 0 |
| 2013-4-3 02:46:40 | 4 | 2013 | 0 |
| 2013-5-3 02:49:15 | 5 | 2013 | 0 |
| 2013-6-3 02:53:36 | 6 | 2013 | 0 |
| 2013-7-6 07:43:27 | 7 | 2013 | 0 |
| 2013-8-3 02:44:36 | 8 | 2013 | 0 |
| 2013-9-3 02:46:40 | 9 | 2013 | 0 |
| 2013-10-3 02:49:15 | 10 | 2013 | 912 |
| 2013-11-3 02:53:36 | 11 | 2013 | 0 |
| 2013-12-6 07:43:27 | 12 | 2013 | 0 |
+---------------------+-------+------+-------+
Is this possible with MysqL ?
Since it's impossible to use sequences in MySQL (actually, they simply do not exist there), you'll have to create your dates range table first. That will be like:
CREATE TABLE dates_range (record_date DATE)
and then fill this table with dates, starting from minimum among dates, that exist in your sale table and till maximum.
After this, using SQL LEFT JOIN operator, you'll be able to aggregate your data like this:
SELECT
YEAR(dates_range.record_date),
MONTH(dates_range.record_date),
DAY(dates_range.record_date),
COALESCE(SUM(sale.total), 0) AS total_sum
FROM
dates_range
LEFT JOIN sale
ON dates_range.record_date=DATE(sale.c_date)
GROUP BY
YEAR(dates_range.record_date),
MONTH(dates_range.record_date),
DAY(dates_range.record_date)
it looks to me that you need an outer join with a calendar table.
Imagine a calendar table populated like:
Calendar
Year Month Day
2013 201310 2013-10-1
2013 201310 2013-10-2
...
Then you can write a query like
select date(c_day) as date,
sum(total) as total_sale
from calendar c
left outer join sale s
on c.day = s.c_date
where c.month = 201310
group by c_day
having c_day <= max(s.c_date); -- this is to avoid to show all
-- days for October

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)