MySQL group by percent with where clause - mysql

i have a table Products
with following fields and values:
ID | Productname | Productprice | Supplier | Available |
+----+-------------+--------------+----------+-----------+
| 1 | Tshirt | 20 | CompanyA | Yes |
| 2 | Pants | 45 | CompanyA | Yes |
| 3 | Shoes | 95 | CompanyB | Yes |
| 4 | Socks | 12 | CompanyA | No |
| 5 | Trainer | 50 | CompanyA | Yes |
How to query how many products belong to CompanyA with status Available=Yes in percentage?

To get the percentage of available products appearing in CompanyA from the overall products, you may use a subquery to calculate that.
SELECT COUNT(*) * 100 / (SELECT COUNT(*) FROM `products`) as `percentage`
FROM `products` WHERE `supplier` = 'CompanyA' and `available` = 'Yes'
Based on your data sample, the above query should return
percentage
+-----+-------+
| 60.0000 |

Considering that by "percent" you mean the number of rows with Available among all rows of that company:
select Supplier, TotalAvailable / Total from(
select
Supplier,
sum( if(Available = 'Yes',1,0) ) as TotalAvailable,
count(*) as Total
from
Products
where
Supplier = 'CompanyA'
group by
Supplier
) a
Or you can use
select
Supplier,
sum( if(Available = 'Yes',1,0) ) / count(*) as Percent
from
Products
where
Supplier = 'CompanyA'
group by
Supplier
Mind that you must handle the "Percent" as it fits for your intentions: multiply by 100, cut the decimals for representation etc.

Related

MySQL Case Conditions Deduction to generate balance

I have a MySQL table named, "store_update_stock" to store purchase & issue of items. Order status column maintain the states, "purchase" or "issue" when need. Purchase quantity has denoted by plus values (eg:-10) & issue quantity has denoted by minus values (eg:- -2) in the table.
To get the purchase & issue summary I used the following query.
SELECT item_id, item_name, order_status, (CASE order_status
WHEN "issue" THEN store_update_stock_details.qty * (-1)
ELSE store_update_stock_details.qty
END) quantity FROM store_update_stock
And generated the expected output as follows :
+---------+-----------+--------------+----------+
| item_id | item_name | order_status | quantity |
+---------+-----------+--------------+----------+
| 1000 | A4 | purchase | 10 |
| 1001 | A3 | purchase | 5 |
| 1000 | A4 | issue | 2 |
| 1000 | A4 | issue | 3 |
| 1001 | A3 | purchase | 6 |
+---------+-----------+--------------+----------+
But I need to get further the balance of each items after performing purchases & issues by modifying the above query and get the output as follows :
+---------+-----------+---------+
| item_id | item_name | balance |
+---------+-----------+---------+
| 1000 | A4 | 5 |
| 1001 | A3 | 11 |
+---------+-----------+---------+
What can be done in my query to get the desired output. Can anyone helpme ?
It looks like all you might need is a simple aggregate sum and a group by:
select item_id, item_name, sum(quantity) as balance
from store_update_stock
group by item_id, item_name
maybe?

MYSQL join query not working as expected

I have basically have two tables:
1) billingcharges : here we store id of a restaurant (restid) , charge id (chargeid), chargetime(timeinmillis when the charge occurred),chargeamount(int amount of the actual charge).charge id is a foreign key to the billingchargedetails table.
2) billingchargedetails: here we store the details of all the possible charges. chargeid(primary key int), chargename (name of the charge), perdaycost (cost per day of the charge)
What i expect:
a summary report of totalamount of charge for each charge for each restaurant.
The current entries inside the tables are:
select * from billingcharges;
+--------+----------+---------------+--------------+
| restid | chargeid | chargetime | chargeamount |
+--------+----------+---------------+--------------+
| 1 | 1 | 1536363636363 | 700 |
| 2 | 1 | 1536363636363 | 500 |
| 1 | 1 | 1568789654123 | 500 |
+--------+----------+---------------+--------------+
select * from billingchargedetails;
+----------+--------------------+------------------+
| chargeid | chargename | chargecostperday |
+----------+--------------------+------------------+
| 1 | Base Charge | 50 |
| 2 | Spotlight Listing | 50 |
| 3 | Gold Notification | 500 |
| 4 | Discount (FIRST50) | 18 |
+----------+--------------------+------------------+
A simple join on chargeid ended up not giving me the qty and sum as expected.so i need some form of a left or right outer join, that much i know and tried
I tried a left join as follows:
select restid, B.chargeid, chargename, count(B.chargeid) as qty,
sum(ifnull(chargeamount,0)) as total
from billingcharges as B
left join billingchargedetails as C on B.chargeid=C.chargeid
group by restid,B.chargeid;
+--------+----------+-------------+-----+-------+
| restid | chargeid | chargename | qty | total |
+--------+----------+-------------+-----+-------+
| 1 | 1 | Base Charge | 2 | 1200 |
| 2 | 1 | Base Charge | 1 | 500 |
+--------+----------+-------------+-----+-------+
This does work and sums things but there are missing charges for each restaurant. even if they arent present inside the billinghcarges ie the left table, i need it with qty 0 and total 0.
I tried a right join and a random value was selected by mysql from the non existing entries inside the left table as follows:
select restid, B.chargeid, chargename, count(B.chargeid) as qty,
sum(ifnull(chargeamount,0)) as total
from billingcharges as B
right join billingchargedetails as C on B.chargeid=C.chargeid
group by restid,B.chargeid;
+--------+----------+-------------------+-----+-------+
| restid | chargeid | chargename | qty | total |
+--------+----------+-------------------+-----+-------+
| NULL | NULL | Spotlight Listing | 0 | 0 |
| 1 | 1 | Base Charge | 2 | 1200 |
| 2 | 1 | Base Charge | 1 | 500 |
+--------+----------+-------------------+-----+-------+
The expected output is something like:
restid chargeid chargename qty totalamount
1 1 Base Charge 2 1200
1 2 Spotlight 0 0
1 3 Gold 0 0
1 4 Discount 0 0
2 1 Base Charge 1 500
2 2 Spotlight 0 0
2 3 Gold 0 0
2 4 Discount 0 0
'same as above expected for each restid in billingcharges'
Before you can do the outer join, you need to generate the cross-product of restaurants to charge types.
Something like the following (but I have not tested it):
SELECT R.restid, D.chargename, COUNT(B.chargeid) AS qty,
SUM(IFNULL(B.chargeamount, 0)) AS total
FROM (SELECT DISTINCT restid FROM billingcharges) AS R
CROSS JOIN billingchargedetails AS D
LEFT JOIN billingcharges AS B ON R.restid=B.restid AND D.chargeid=B.chargeid
GROUP BY R.restid, D.chargename;
In this example, the cross-product of R and D is every restaurant crossed with every charge type.
Of course not all of those charges exist for every restaurant. So the outer join to billingcharges finds those rows that do exist for each respective combination of restaurant & charge type.

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

using HAVING to filter results based on a reference row

I have the following table:
+---------+--------------+----------+
| item_id | location_id | price |
+---------+--------------+----------+
| 1 | 1 | 100 |
| 1 | 1 | 250 |
| 1 | 2 | 50 |
| 2 | 1 | 250 |
| 2 | 1 | 1000 |
| 3 | 1 | 1000 |
| 3 | 2 | 100 |
+---------+--------------+----------+
I can reduce this down to the minimum values using this query
SELECT
item_id, location_id, MIN(price) AS Price
from
table
GROUP BY item_id , location_id
This gets me
+---------+--------------+----------+
| item_id | location_id | price |
+---------+--------------+----------+
| 1 | 1 | 100 |
| 1 | 2 | 50 |
| 2 | 1 | 250 |
| 3 | 1 | 1000 |
| 3 | 2 | 100 |
+---------+--------------+----------+
I want to reduce this further. I am using the rows with a location_id of 1 as a reference row. For each row that has an item_id matching the reference row's item_id but a different location id. I want to compare that row's price with the reference row's price. If the price is lower than the reference row's price, I want to filter that row out.
My final result should include the reference row for each item id and any rows that met the criteria of the price being lower than the reference row price.
I have a hunch that I can use the HAVING clause to do this but I am having trouble compiling the statement. How should I construct the HAVING statement?
Thanks in advance
Nah, having can't help you like this, having is for things like you need filter min() result for something
e.g:
select id,min(price) from table where date = '2016-3-18' group by id having min(price) = 50
it will show you the records that min(price)=50
let's back to your case, there are lots of way to do that,
1. left join
select a.item_id,a.location_id,a.price
from table a
left join table b
on a.location_id = b.location_id and a.price > b.price
where b.price is null
2. exists
select a.item_id,a.location_id,a.price
from table a
where exists(
select 1 from
(select location_id,min(price)as price from table group by location_id)b
where a.location_id = b.location_id and a.price = b.price
)
normally i ll recommand you use exists