MySQL SUM() on identical cells in different columns - mysql

I have a database on which I run the following query:
SELECT products.name AS 'Product',
products_used.amount * procedures_perf.amount AS 'Total'
FROM products_used
INNER JOIN procedures ON procedures.pr_id = products_used.pr_id
INNER JOIN products ON products.p_id = products_used.p_id
INNER JOIN procedures_perf ON procedures_perf.pr_id = products_used.pr_id
INNER JOIN hospitals ON hospitals.h_id = procedures_perf.h_id
WHERE hospitals.h_id = "20001"
This produces the following excerpt:
Product Total
-----------------
retractor 402
EB 402
EB 0
retractor 105
EB 0
So there are a number of procedures, which all use a specific set of products. I am trying to select the amount of total products used, whereas it is giving me a list of each product used per procedure. The outcome I would want is this:
Product Total
-----------------
retractor 507
EB 402
I have tried to use SUM() on the multiplication, but it keeps grabbing the total of all products. The only solution I could find online is one where the "Product" field would have to be listed in an IF-clause, but I do not want to hardcore the product names in an IF-statement, as it is an ever expanding list. I am probably overlooking something really obvious...
JUST TO BE CLEAR: Group by products.name is not adding up the total values, it just takes the first value it gets. SUM(x * y) suggestion is throwing out an obscenely large number.

You need to use of group by-
SELECT products.name AS 'Product',
SUM(products_used.amount * procedures_perf.amount) AS 'Total'
FROM products_used
INNER JOIN procedures ON procedures.pr_id = products_used.pr_id
INNER JOIN products ON products.p_id = products_used.p_id
INNER JOIN procedures_perf ON procedures_perf.pr_id = products_used.pr_id
INNER JOIN hospitals ON hospitals.h_id = procedures_perf.h_id
WHERE hospitals.h_id = "20001"
GROUP BY products.name;

Use GROUP BY
SELECT product, SUM(total) as total
FROM tablename
GROUP BY product;
After changing your whole query
SELECT products.name AS 'Product',
SUM(products_used.amount * procedures_perf.amount) AS 'Total'
FROM products_used
INNER JOIN procedures ON procedures.pr_id = products_used.pr_id
INNER JOIN products ON products.p_id = products_used.p_id
INNER JOIN procedures_perf ON procedures_perf.pr_id = products_used.pr_id
INNER JOIN hospitals ON hospitals.h_id = procedures_perf.h_id
WHERE hospitals.h_id = "20001"
GROUP BY products.name;

Related

Multi INNER JOIN gives unexpected duplicate records

Running the following SELECT query gives unexpectedly two times the same record while there is only 1 product in the database. The are however multiple subcategories linked to the same category, but I still don't understand why this would give two results.
The ERD:
The full contents of the DB:
SELECT p.id AS productId, p.name AS productName FROM product p
INNER JOIN product_base AS pb ON pb.id = p.product_base_id
INNER JOIN product_category AS pc ON pc.id = pb.product_category_id
INNER JOIN product_subcategory AS psc ON psc.product_category_id = pc.id;
Returns:
Why is this product returned two times?
Appending WHERE psc.id = 2 will still give one product as a result, while the intention is that this product should only be found when psc.id = 1.
What am I missing here? Is there something wrong with the structure? How would I get all products that have a certain subcategory?
Would I need to store product_category_id and product_subcategory_id directly in product as well?
#barmar made me realize I am simply missing a direct FK from product to product_subcategory. Otherwise there is of course a missing link between the product and subcategory.
DISTINCT will filter out the duplicates.
SELECT DISTINCT p.id AS productId, p.name AS productName
FROM product p
INNER JOIN product_base AS pb ON pb.id = p.product_base_id
INNER JOIN product_category AS pc ON pc.id = pb.product_category_id
INNER JOIN product_subcategory AS psc ON psc.product_category_id = pc.id;

Mysql (doctrine) - count inner join having count > X

I have SQL to count products with specific properties. I am using it in the products filter. SQL is very long, but here is the primary part:
SELECT COUNT(products.id) as products_count, property_items.description, property_items.id as id
FROM property_items
INNER JOIN product_properties ON property_items.id = product_properties.property_item_id
INNER JOIN products ON product_properties.product_id
INNER JOIN product_properties pp ON products.id = pp.product_id AND (pp.property_item_id IN ($ids))
GROUP BY property_items.id
HAVING COUNT(pp.id) >= $countIds
This works perfectly when I have only the one element in $ids, but when i choose one more, the result is bad. It looks like the sql returns count of all products with any property from $ids, but I need to count only products that contains all properties.
First get all available properties. On each property join products that contains this property and go back to all properties of this product to check, if product contains already checked properties too. Or it is bad idea? I need to keep primary table (FROM table) as property_items.
I need to get result in this format:
=============================
id|description|products_count
=============================
1 |lorem ipsum|10
-----------------------------
2 |dolore sit |2
Thanks for any idea.
Try to use SELECT COUNT (DISTINCT products.id) as cnt
You can get the product ids that have all the properties by doing:
SELECT pp.property_id
FROM property_items pi INNER JOIN
product_properties pp
ON pi.id = pp.property_item_id INNER JOIN
products p
ON pp.product_id = p.id
WHERE pp.property_item_id IN ($ids)
GROUP BY pp.property_id
HAVING COUNT(DISTINCT pp.property_item_id) = $countIds -- has all of them
Note that I rationalized the joins. I think your simplification of the query wasn't quite right. I also added table aliases, so the query is easier to write and to read.
If you want the count of such products, use a subquery:
SELECT COUNT(*)
FROM (SELECT pp.property_id
FROM property_items pi INNER JOIN
product_properties pp
ON pi.id = pp.property_item_id INNER JOIN
products p
ON pp.product_id = p.id
WHERE find_in_set(pp.property_item_id, $ids)
GROUP BY pp.property_id
HAVING COUNT(DISTINCT pp.property_item_id) = $countIds -- has all of them
) ;
Your problem is probably because of this line:
WHERE pp.property_item_id IN ($ids)
If you are passing $ids as a comma-separated string, then your query will not work. Note the replacement above.

MySQL JOIN tables with COUNT values

I have the following tables in my database.I only listed the important columns which can be used for joining.
I need to get the following output
Currently I'm using two seperate queries for each COUNT value
For assigned licenses
select
products.id,products.name,COUNT(assigned_licenses.id)
from
deployment_users
inner join
assigned_licenses
on
deployment_users.id = assigned_licenses.deployment_user_id
inner join
products
on
assigned_licenses.id = products.id
and
deployment_users.customer_id = 10
group by
assigned_licenses.id
;
For total licenses
select
products.id,products.name,COUNT(total_licenses.id)
from
customers
inner join
total_licenses
on
customers.iccode = licenses.iccode
inner join
products
on
total_licenses.id = products.id
and
customers.id = 10
group by
total_licenses.id
;
Since there are more than a 1,000 products that need to be listed,I want to combine them into a single query.How can I do that?
Your specification leaves some room for interpretation (e.g. can a user have assigned licenses without total licenses? if yes my query will fail.) but I would go with this.
SELECT
products.id,
products.name,
Count(Distinct total_licenses.id) As CountTotalLicenses,
Count(Distinct assigned_liceses.deployment_users_id) As CountAssignedLicenses
FROM products
LEFT JOIN total_licenses ON total_licenses.products_id = products.id
LEFT JOIN customers ON customers.iccode = total_licenses.customers_iccode
LEFT JOIN assigned_licenses ON assigned_liceses.total_licenses_id = total_licenses.id
WHERE
customers.id = 10
GROUP BY
products.id,
products.name
For the future it would be awesome if you could paste code as code and not as an image. People cannot simple copy paste snippets of your code and have to type everything again...
Try joining Both of your query
SELECT * FROM (
(First Query) as assigned_licn
INNER JOIN
(Second Query) as total_licn
USING (id)
);

Getting info through 3 tables

I'm following the SQL tutorial from w3schools.
I want to get the value of all orders delivered by a shipper. I don't have any idea about how I can get these details as the info are in different tables and the INNER JOIN didn't worked for me.
Database: http://www.w3schools.com/sql/trysql.asp?filename=trysql_select_groupby
By now, I managed to get the number of orders by each shipper.
SELECT Shippers.ShipperName,COUNT(Orders.OrderID) AS NumberOfOrders FROM Orders
LEFT JOIN Shippers
ON Orders.ShipperID=Shippers.ShipperID
GROUP BY ShipperName;
How could I get the value of those?
To bring the Price of a Product into your query you will need to join in tables OrderDetails to the Order table on the Orders.Id and then join in the Products table to the OrderDetail table on ProductID
SELECT Shippers.ShipperName,
COUNT(Orders.OrderID) AS NumberOfOrders,
Sum(Products.price * OrderDetails.Quantity) AS SumOfPrice
FROM Orders
LEFT JOIN Shippers ON Orders.ShipperID = Shippers.ShipperID
LEFT JOIN OrderDetails ON ORders.OrderID = OrderDetails.OrderID
LEFT JOIN Products ON OrderDetails.ProductID = Products.ProductID
GROUP BY ShipperName;
I just stuck with LEFT JOIN here as your example used, but an INNER JOIN would work just as well and be more efficient.
The FROM clause of the SQL statement is one of the first parts of the SQL to run against your database. It establishes which tables we are grabbing information from and the relationship between those tables (using the ON keyword). So here we bring in 4 tables, and use the ON keyword to show the relationship between all of them using their respective IDs. Then we can add their fields to the SELECT portion of the SQL statement and aggregate where needed.
If you want the "sum" of the product prices, that would be very similar to what you already have. Note how you currently use the COUNT() function to get the count, you can use the SUM() function to get the total of any numeric column.
Something like this:
SELECT
Shippers.ShipperName,
COUNT(Orders.OrderID) AS NumberOfOrders,
SUM(Products.Price) AS PriceOfOrders
FROM
Orders
INNER JOIN Shippers ON Orders.ShipperID = Shippers.ShipperID
INNER JOIN OrderDetails ON Orders.OrderID = OrderDetails.OrderID
INNER JOIN Products ON OrderDetails.ProductID = Products.ProductID
GROUP BY
ShipperName
Or perhaps the price also needs to be multiplied by the quantity in this calculation? Something like this:
SELECT
Shippers.ShipperName,
COUNT(Orders.OrderID) AS NumberOfOrders,
SUM(Products.Price * OrderDetails.Quantity) AS PriceOfOrders
FROM
Orders
INNER JOIN Shippers ON Orders.ShipperID = Shippers.ShipperID
INNER JOIN OrderDetails ON Orders.OrderID = OrderDetails.OrderID
INNER JOIN Products ON OrderDetails.ProductID = Products.ProductID
GROUP BY
ShipperName
It's up to your understanding of the table structure and the data, really. But the concept is the same, grouping by a value and applying a calculation to the grouped values (count or sum).

Calculate percentage of false records to total records within a aggregate function

I'm looking for a solution to calculate a percentage rate of a boolean value:
Model:
Products 1 <-> n Components
Components.essential is a boolean attribute
What I want to calculate is something like:
SELECT products.*, count(comp_a.essential)/count(comp_b.essential) AS essential_percentage
From products
INNER JOIN components AS comp_a ON products.id = comp_a.product_id
INNER JOIN components AS comp_b ON products.id = comp_b.product_id
WHERE comp_a.essential = 1
GROUP BY products.id
Unfortunately this statement calculates way too many components as there are two INNER JOIN on the components model...
Any help on this?
Markus
Only need one join. Note the standard GROUP BY clause.
This gives "true" percent because I assume that's what you mean (not false)
SELECT
p.*,
count(CASE WHEN c.essential = 1 THEN 1 END) / count(*) AS essential_percentage
From
products p
LEFT OUTER JOIN
components c ON p.id = c.product_id
GROUP BY
p.*;
or
count(nullif(c.essential, 0)) / count(*) AS essential_percentage