MySQL, SELECT column value when using GROUP BY and HAVING - mysql

I want to SELECT the asset_id, category_name, and Fixture ID for assets with 2 or less properties (2 or less rows in asset_property table). So far all I have is the asset_id and category_name. The Fixture ID may not exist for all assets. If it does, it would be the property_value from a row with property_id 1
EXAMPLE OF DESIRED RESULTS
asset_property table:
| asset_id | property_id | property_value |
|:--------:|:-----------:|:---------------|
| 100 | 1 | A-6 |*
| 100 | 6 | Blue |
| 104 | 1 | CC-7 |*
| 104 | 6 | Blue |
| 104 | 4 | 12" |
| 105 | 1 | B-1 |*
| 108 | 1 | HR-1 |*
| 109 | 3 | 500 |
| 109 | 4 | 1" |
*property_id 1 is the Fixture ID
Desired results:
| asset_id | category_name | property_value*|
|:--------:|:-------------:|:---------------|
| 100 | Flooring | A-6 |
| 104 | Flooring | CC-7 |
| 105 | Kitchen Equip | B-1 |
| 108 | Plumbing | HR-1 |
| 109 | Plumbing | |
*property_value is the Fixture ID if the property_id is 1
MY MYSQL SO FAR
MySQL:
SELECT asset_property.asset_id, category_name
FROM asset_property
LEFT JOIN asset
ON asset.asset_id = asset_property.asset_id
LEFT JOIN category
ON category.category_id = asset.category_id
GROUP BY asset_property.asset_id
HAVING COUNT(asset_property.asset_id) <= 2
Results from MySQL:
| asset_property.asset_id | category_name |
|:-----------------------:|:--------------|
| 3048 | Parking Lot |
| 3519 | Kitchen |
| 3522 | Kitchen |
| 3597 | Flooring |
(etc...)
So I need to also SELECT the property_value WHERE the property_id = 1. I hope this makes sense, Thanks!

Maybe switch/case would be appropriate for this query
SELECT asset_property.asset_id, category_name,
( CASE property_id
WHEN 1 THEN property_value
ELSE 0
END
)
FROM asset_property
LEFT JOIN asset
ON asset.asset_id = asset_property.asset_id
LEFT JOIN category
ON category.category_id = asset.category_id
GROUP BY asset_property.asset_id
HAVING COUNT(asset_property.asset_id) <= 2
Hope that helps..

Related

GROUP sum BY two tables by joining

I'm trying to write a SQL query that will correctly group sales items sold_qyt and sub-total-price together as per product's category so I can show this on the printable invoice that product from Jelly Sheet = 4 at a rate of 62 subtotal for this category product is 248(4 * 62 = 248). but when I try to run the below-mentioned query it shows out-put as 12 but I want subtotal and sold_qyt segregated base on category.
I have tried to run different queries just one query gives the output which is mentioned below and this is for just the sum of all sold_qyt. DB example is also shown below
DB Example: (For better understanding)
Table # 1:
Category
ID | code | name
1 | 1 | jelly sheet
2 | 2 | 9D Glass
3 | 3 | Polished Glass
Table # 2:
Product:
ID | code | name | cost | category_id | price
1 | 1 | IP11JS | 50 | 1 | 62
2 | 2 | IP12JS | 50 | 1 | 62
3 | 3 | IP119D | 40 | 2 | 55
4 | 4 | IP129D | 40 | 2 | 55
5 | 5 | IP11PG | 18 | 3 | 25
6 | 6 | IP12PG | 18 | 3 | 25
Table # 3:
sale_items:
ID | sale_id | product_id | product_code | product_name | unit_price | sold_qyt | subtotal |
1 | 1 | 1 | 1 | IP11JS | 62 | 2 | 124 |
2 | 1 | 2 | 2 | IP12JS | 62 | 2 | 124 |
3 | 1 | 3 | 3 | IP119D | 55 | 2 | 110 |
4 | 1 | 4 | 4 | IP129D | 55 | 2 | 110 |
5 | 1 | 5 | 5 | IP11PG | 25 | 2 | 50 |
6 | 1 | 6 | 6 | IP12PG | 25 | 2 | 50 |
7 | 2 | 7 | 1 | IP11JS | 62 | 2 | 124 |
8 | 2 | 8 | 2 | IP12JS | 62 | 2 | 124 |
9 | 2 | 9 | 3 | IP119D | 55 | 2 | 110 |
10 | 2 | 10 | 4 | IP129D | 55 | 2 | 110 |
11 | 2 | 11 | 5 | IP11PG | 25 | 2 | 50 |
12 | 2 | 12 | 6 | IP12PG | 25 | 2 | 50 |
SQL Query which is run by me:
SELECT sale_id,
SUM(sold_qyt) AS sold_qyt
FROM sale_items
GROUP BY sale_id
kindly help me with this difficulty thanks in advance
Update: 1-21-2021
i execute new query
SELECT (sma_sale_items.sale_id, sma_categories.code AS sma_products.category_id, sma_products.code AS sma_sale_items.product_code,)
SUM(sold_qyt) AS sold_qyt
SUM(subtotal) AS subtotal
FROM sma_sale_items
LEFT JOIN sma_products ON sma_products.id=sma_sale_items.product_id
LEFT JOIN sma_categories ON sma_categories.code=sma_products.category_id
GROUP BY sma_sale_items.sale_id
ORDER BY sma_categories
but no luck :(
I want the output like this:
Expected OUT PUT:
ID | sale_id | category_name | sold_qyt | subtotal |
1 | 1 | Jelly Sheet | 4 | 248 |
2 | 1 | 9D Glass | 4 | 220 |
3 | 1 | Polished Glass | 4 | 100 |
4 | 2 | Jelly Sheet | 4 | 248 |
5 | 2 | 9D Glass | 4 | 220 |
6 | 2 | Polished Glass | 4 | 100 |
The ID column in your expected result set is very misleading - it appears to be just new ID value for the output result set rather than any of the ID values from the source tables.
If it is important for you then you can use this query:
SELECT ROW_NUMBER() OVER (ORDER BY sale_id, category_id),
sale_id,
category_name,
sold_qty,
subtotal
FROM (
SELECT c.ID as category_id,
si.sale_id,
c.[name] as category_name,
SUM(si.sold_qty) as sold_qty,
SUM(si.subtotal) as subtotal
FROM sale_items si
JOIN product p ON p.ID = si.product_code
JOIN category c ON c.ID = p.category_id
GROUP BY c.ID,
si.sale_id,
c.[name]
) r
If it is not relevant and you only want the sale_id, category_name and the totals then simplify it to:
SELECT si.sale_id,
c.[name] as category_name,
SUM(si.sold_qty) as sold_qty,
SUM(si.subtotal) as subtotal
FROM sale_items si
JOIN product p ON p.ID = si.product_code
JOIN category c ON c.ID = p.category_id
GROUP BY si.sale_id,
c.[name]
ORDER BY sale_id, category_name

How to perform full text search in MySQL joining multiple tables

I'm creating e-commerce web site using MySQL. I have successfully created and inserted data to database.
Here is my database schema
table: categories table: product_types
+----+--------------+ +----+-------------+------------+
| id | name | | id | category_id | name |
+----+--------------+ +----+-------------+------------+
| 1 | Electronics | | 1 | 1 | Smartphone |
| 2 | Fashion | | 2 | 1 | Speakers |
+----+--------------+ +----+-------------+------------+
table: products
+----+-----------------+-------------+-------------------+-------+
| id | product_type_id | category_id | name | price |
+----+-----------------+-------------+-------------------+-------+
| 1 | 1 | 1 | Samsung Galaxy A3 | 300 |
| 2 | 1 | 1 | Samsung Galaxy A7 | 400 |
+----+-----------------+-------------+-------------------+-------+
table: options table: option_values
+----+-----------------+-------+ +----+-----------+------------+
| id | product_type_id | name | | id | option_id | name |
+----+-----------------+-------+ +----+-----------+------------+
| 1 | 1 | RAM | | 1 | 1 | 512 MB |
| 2 | 1 | Screen| | 2 | 1 | 1 GB |
| 3 | 1 | OS | | 3 | 3 | Android 5 |
+----+-----------------+-------+ | 4 | 3 | Android 6 |
| 5 | 2 | HD |
| 6 | 2 | FHD |
+----+-----------+------------+
table: product_option_values
+----+------------+-----------+-----------------+
| id | product_id | option_id | option_value_id |
+----+------------+-----------+-----------------+
| 15 | 1 | 1 | 1 |
| 16 | 1 | 2 | 5 |
| 17 | 1 | 3 | 3 |
| 18 | 2 | 1 | 2 |
| 19 | 2 | 2 | 6 |
| 20 | 2 | 3 | 4 |
+----+------------+-----------+-----------------+
Search must trigger through name column of each table and return name and price from products table.
The problem is that I don't know how to perform full text search joining all that tables.
Is there any easy way to do it?
You need a query that LEFT JOINs on each table to search with a condition based on fulltext search function MATCH, with a WHERE clause to filter out non-matching records. The SELECT DISTINCT ensures that you will not see duplicates.
We need to adjust manually the JOIN criteria from each table to products : option_values is the most complicated case as it does not directly references products (an additional join on product_option_values is needed, aliased pov below.
SELECT DISTINCT p.name, p.price
FROM
products p
LEFT JOIN categories c
ON MATCH(c.name) AGAINST('foo' IN NATURAL LANGUAGE MODE)
AND c.id = p.category_id
LEFT JOIN product_types pt
ON MATCH(pt.name) AGAINST('foo' IN NATURAL LANGUAGE MODE)
AND pt.category_id = p.category_id
LEFT JOIN options o
ON MATCH(o.name) AGAINST('foo' IN NATURAL LANGUAGE MODE)
AND o.product_type_id = p.product_type_id
LEFT JOIN product_option_values pov
ON pov.product_id = p.id
LEFT JOIN option_values ov
ON MATCH(ov.name) AGAINST('foo' IN NATURAL LANGUAGE MODE)
AND ov.id = pov.option_value_id
WHERE
COALESCE(c.id, pt.id, o.id, ov.id) IS NOT NULL

Subtract two columns of different tables with different number of rows

How can I write a single query that will give me SUM(Entrance.quantity) - SUM(Buying.quantity) group by product_id.
The problem is in rows that not exist in the first or second table. Is possible to do this?
Entrance:
+---+--------------+---------+
| id | product_id | quantity|
+---+--------------+---------+
| 1 | 234 | 15 |
| 2 | 234 | 35 |
| 3 | 237 | 12 |
| 4 | 237 | 18 |
| 5 | 101 | 10 |
| 6 | 150 | 12 |
+---+--------------+---------+
Buying:
+---+------------+-------------+
| id | product_id | quantity|
+---+------------+-------------+
| 1 | 234 | 10 |
| 2 | 234 | 20 |
| 3 | 237 | 10 |
| 4 | 237 | 10 |
| 5 | 120 | 15 |
+---+------------+------------+
Desired result:
+--------------+-----------------------+
| product_id | quantity_balance |
+--------------+-----------------------+
| 234 | 20 |
| 237 | 10 |
| 101 | 10 |
| 150 | 12 |
| 120 | -15 |
+--------------+-----------------------+
This is tricky, because products could be in one table but not the other. One method uses union all and group by:
select product_id, sum(quantity)
from ((select e.product_id, quantity
from entrance e
) union all
(select b.product_id, - b.quantity
from buying b
)
) eb
group by product_id;
SELECT product_id ,
( Tmp1.enterquantity - Tmp2.buyquantity ) AS Quantity_balance
FROM entrance e1
CROSS APPLY ( SELECT SUM(quantity) AS enterquantity
FROM Entrance e2
WHERE e1.product_id = e2.product_id
) Tmp1
CROSS APPLY ( SELECT SUM(quantity) AS buyquantity
FROM Buying b2
WHERE e1.product_id = b2.product_id
) Tmp2
GROUP BY Product_id,( Tmp1.enterquantity - Tmp2.buyquantity )

Querying across 6 tables, is there a better way of doing this?

What I did was, I wanted each user to have their own "unique" numbering system. Instead of auto incrementing the item number by 1, I did it so that Bob's first item would start at #1 and Alice's number would also start at #1. The same goes for rooms and categories. I achieved this by creating "mapping" tables for items, rooms and categories.
The query below works, but I know it can definitely be refactored. I have primary keys in each table (on the "ids").
SELECT unique_item_id as item_id, item_name, category_name, item_value, room_name
FROM
users_items, users_map_item, users_room, users_map_room, users_category, users_map_category
WHERE
users_items.id = users_map_item.map_item_id AND
item_location = users_map_room.unique_room_id AND
users_map_room.map_room_id = users_room.room_id AND
users_map_room.map_user_id = 1 AND
item_category = users_map_category.unique_category_id AND
users_map_category.map_category_id = users_category.category_id AND
users_category.user_id = users_map_category.map_user_id AND
users_map_category.map_user_id = 1
ORDER BY item_name
users_items
| id | item_name | item_location |item_category |
--------------------------------------------------------
| 1 | item_a | 1 | 1 |
| 2 | item_b | 2 | 1 |
| 3 | item_c | 1 | 1 |
users_map_item
| map_item_id | map_user_id | unique_item_id |
----------------------------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
users_rooms
| id | room_name |
----------------------
| 1 | basement |
| 2 | kitchen |
| 3 | attic |
users_map_room
| map_room_id | map_user_id | unique_room_id |
----------------------------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
users_category
| id | room_name |
----------------------
| 1 | antiques |
| 2 | appliance |
| 3 | sporting goods |
users_map_category
| map_room_id | map_user_id | unique_category_id |
----------------------------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
Rewriting your query with explicit JOIN conditions makes it more readable (while doing the same).
SELECT mi.unique_item_id AS item_id
, i.item_name
, c.category_name
, i.item_value
, r.room_name
FROM users_map_item mi
JOIN users_items i ON i.id = mi.map_item_id
JOIN users_map_room mr ON mr.unique_room_id = i.item_location
JOIN users_room r ON r.room_id = mr.map_room_id
JOIN users_map_category mc ON mc.unique_category_id = i.item_category
JOIN users_category c ON (c.user_id, c.category_id)
= (mc.map_user_id, mc.map_category_id)
WHERE mr.map_user_id = 1
AND mc.map_user_id = 1
ORDER BY i.item_name
The result is unchanged. Query plan should be the same. I see no way to improve the query further.
You should use LEFT [OUTER] JOIN instead of [INNER] JOIN if you want to keep rows in the result where no matching rows are found in the right hand table. You may want to move the additional WHERE clauses to the JOIN condition in this case, as it changes the outcome.

Include third table in LEFT JOIN query

I have five mysql tables. shops
+----+--------------+--------------+
| id | name | address |
+----+--------------+--------------+
| 1 | Shop1 | Street1 |
| 2 | Shop2 | Street2 |
| 3 | Shop3 | Street3 |
| 4 | Shop4 | Street4 |
+----+--------------+--------------+
fruits
+----+--------------+--------------+
| id | fruit | price |
+----+--------------+--------------+
| 1 | Bannana | 2.5 |
| 2 | Apple | 2.1 |
| 3 | Orange | 1.8 |
| 4 | Plum | 2.2 |
+----+--------------+--------------+
availability
+----+--------------+--------------+
| id | shop_id | fruit_id |
+----+--------------+--------------+
| 1 | 1 | 2 |
| 2 | 2 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
+----+--------------+--------------+
shop_activity
+----+--------------+--------------+--------------+
| id | shop_id | user_id | status |
+----+--------------+--------------+--------------+
| 1 | 2 | 1 | 1 |
| 2 | 3 | 2 | 1 |
| 3 | 1 | 2 | 2 |
| 4 | 2 | 2 | 1 |
+----+--------------+--------------+--------------+
users
+----+--------------+
| id | name |
+----+--------------+
| 1 | Peter |
| 2 | John |
+----+--------------+
I have query
SELECT
availability.shop_id,
shops.name
FROM availability
LEFT JOIN shops
ON availability.shop_id=shops.id
WHERE
fruit_id = 2
As a result I get name list of shops where fruit with id 2 (apple) is available.
What should I do so that I can include shop_activity table in query to get user's status if users.id = 1 beside proper shop. Something like this...
Shop1, NULL
Shop2, status: 1
You could try something like this:
SELECT
availability.shop_id,
shops.name,
shop_activity.status
FROM availability
LEFT JOIN shops
ON availability.shop_id=shops.id
LEFT JOIN shop_activity
ON shop_activity.shop_id = availability.shop_id
and shop_activity.user_id = 1
WHERE
fruit_id = 2
SELECT
availability.shop_id,
shops.name
FROM shops
LEFT JOIN availability ON availability.shop_id=shops.id
LEFT JOIN shop_activity ON shop_activity .shop_id=shops.id
WHERE
fruit_id = 2
and users.id=1
try making shops as the first table in left join
Try the following:
SELECT shops.name, shop_activity.status
FROM shops
INNER JOIN availability ON availability.shop_id = shops.id
AND availability.fruit_id = 2
LEFT JOIN shop_activity ON shops.shop_id = shop_activity.shop_id
AND shop_activity.user_id = 1
This should give you a row for every shop with apples, but the status will show as null for shops where the user has no activity, otherwise shows the status of that user.