MySQL join query only one row with reference table - mysql

Here is my 5 tables.
product
id(PK)
name
description
id(PK)
body
price
id(PK)
currency
product_description
id(PK)
product_id
description_id
product_price
id(PK)
product_id
price_id
Table product, description, price is where stored actual data.
And table product_description and product_price is reference table.
My expected query result like this.
product_id | product_name | description_body | price_currency
Current table's data
product
+----+----------------+
| id | name |
+----+----------------+
| 1 | first product |
| 2 | second product |
| 3 | third product |
+----+----------------+
description
+----+------------+
| id | body |
+----+------------+
| 1 | first desc |
| 2 | second des |
| 3 | third desc |
+----+------------+
price
+----+-------------+
| id | currency |
+----+-------------+
| 1 | first cur |
| 2 | second cur2 |
| 3 | third cur |
+----+-------------+
product_description
+----+------------+----------------+
| id | product_id | description_id |
+----+------------+----------------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
+----+------------+----------------+
product_price
+----+------------+----------+
| id | product_id | price_id |
+----+------------+----------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
+----+------------+----------+
Query
SELECT product.*, description.body, price.currency FROM product
LEFT JOIN product_description ON product.id = product_description.product_id
LEFT JOIN product_price ON product.id = product_price.product_id
LEFT JOIN description ON description.id = product_description.description_id
LEFT JOIN price ON price.id = product_price.price_id
Result
+----+----------------+------------+-------------+
| id | name | body | currency |
+----+----------------+------------+-------------+
| 1 | first product | first desc | first cur |
| 2 | second product | second desc| second cur2 |
| 3 | third product | third desc | third cur |
+----+----------------+------------+-------------+
In that moment, If I insert into product_description one more row that has product_id=1 and re querying, it shows many rows that product_id=2.
After insert into product_description values(4, 2, 1)
product_description
+----+------------+----------------+
| id | product_id | description_id |
+----+------------+----------------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
| 4 | 2 | 1 |
+----+------------+----------------+
After join query.
+----+----------------+------------+-------------+
| id | name | body | currency |
+----+----------------+------------+-------------+
| 1 | first product | first desc | first cur |
| 2 | second product | second desc | second cur2 |
| 2 | second product | first desc | second cur2 |
| 3 | third product | third desc | third cur |
+----+----------------+------------+-------------+
You know that because of product=2 row's count is 2 in product_description, result print all of it.
But I want to only last one rows that contain product=2.
Maybe limit or distinct is useful, I don't know it is possible.
Can I use limit or distinct in join query?

You can try using correlated subquery
SELECT product.*, description.body, price.currency FROM product
LEFT JOIN
( select * from product_description
where product_description.id in (select max(id) from product_description b
where product_description.product_id=b.product_id)
) as x ON product.id = x.product_id
LEFT JOIN product_price ON product.id = product_price.product_id
LEFT JOIN description ON description.id = x.description_id
LEFT JOIN price ON price.id = product_price.price_id

Related

Get those items which are ordered after they have been delivered

I have two tables, namely itemOrders and itemDelivered.
itemOrders
+-------+---------+--------+
| id | orderid | itemid |
+-------+---------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
| 5 | 3 | 1 |
| 6 | 3 | 2 |
+-------+---------+--------+
And
itemDelivered
+-------+-------------+--------+
| id | orderId | itemid |
+-------+-------------+--------+
| 1 | 2 | 2 |
| 2 | 3 | 2 |
| 3 | 2 | 1 |
+-------+-------------+--------+
From the above scenario I want all those distinct items whose max orderId in the table itemDelivered is less than max orderId in the table itemOrders.
In the above example I should get itemid 1 as the result, as it's max orderid is 2 in table itemDelivered, which is less than its max orderid in table itemOrders which is 3.
I wrote the following query but it gives me both the items, 1 and 2 as item No. 2 doesn't have orderId 1 in itemDelivered table.
SELECT DISTINCT( itemid )
FROM itemorders
WHERE orderid NOT IN (SELECT orderid
FROM itemdelivered)
You can LEFT JOIN between the two table using itemid, and GROUP BY on the itemid.
Eventually use HAVING clause to consider only those itemid values, where MAX(itemdelivered.orderid) < MAX(itemorders.orderid)
View on DB Fiddle
SELECT io.itemid
FROM itemorders AS io
LEFT JOIN itemdelivered AS id
ON id.itemid = io.itemid
GROUP BY io.itemid
HAVING MAX(id.orderid) < MAX(io.orderid)
OR MAX(id.orderid) IS NULL
Result
| itemid |
| ------ |
| 1 |
Ok, manage to write a query which gives the desired output.
SELECT io.itemid
FROM itemorders as io
LEFT JOIN itemdelivered AS id ON io.orderid = id.orderid AND io.itemid = id.itemid
WHERE id.itemid IS NULL
HAVING MAX(id.orderid) IS NULL
ORDER BY io.id

MYSQL Join returns duplicate column name

I am new to SQL and have the following challenge, I have a table on the left that has products assigned to a single company and denoted by a ref_id (it comes from another table, and I have simplified it for sake of question) and I have a master list. I want to compare all the products for company "A" against the master list and show all the products company A has not subscribed to. I am using JOIN and end up getting duplicate column names which throws my PHP program off. Here is what I have tried several syntax for UNION, RIGHT JOIN, LEFT JOIN and NOT operators, and the below is the closest I have gotten, however I am getting duplicate column names and I only need the last table with the last 2 columns. Please advise.
mysql> select ref_id, product_id, short_name from ap_company_product order by ref_id, product_id;
+--------+------------+------------+
| ref_id | product_id | short_name |
+--------+------------+------------+
|      2 | 10         | product 10 |
|      2 | 11         | product 11 |
|      2 | 12         | product 12 |
|      2 | 15         | product 15 |
|      2 | 17         | product 17 |
|      2 | 21         | product 21 |
|      3 | 11         | product 11 |
|      3 | 13         | product 13 |
|      3 | 17         | product 17 |
|      3 | 20         | product 20 |
+--------+------------+------------+
10 rows in set (0.00 sec)
THE MAIN LIST
mysql> select  product_id, short_name from ap_company_product_list;
+------------+-------------+
| product_id | short_name  |
+------------+-------------+
| 10         | product 10  |
| 11         | product 11  |
| 12         | product 12  |
| 13         | product 13  |
| 14         | product 14  |
| 15         | product 15  |
| 16         | product 16  |
| 17         | product 17  |
| 18         | product 18  |
| 19         | product 19  |
| 20         | product 20  |
| 21         | product 21  |
| 22         | product 22  |
+------------+-------------+
13 rows in set (0.00 sec)
Another SQL which is the actual results I need
SELECT aa.product_id as product_id, aa.short_name as short_name, aa.ref_id as ref_id, bb.product_id as product_id, bb.short_name as short_name FROM (select ref_id, product_id, short_name from ap_company_product where ref_id=2) aa RIGHT OUTER JOIN ap_company_product_list bb ON aa.product_id = bb.product_id where aa.product_id is NULL;
+------------+------------+--------+------------+------------+
| product_id | short_name | ref_id | product_id | short_name |
+------------+------------+--------+------------+------------+
| NULL       | NULL       |   NULL | 13         | product 13 |
| NULL       | NULL       |   NULL | 14         | product 14 |
| NULL       | NULL       |   NULL | 16         | product 16 |
| NULL       | NULL       |   NULL | 18         | product 18 |
| NULL       | NULL       |   NULL | 19         | product 19 |
| NULL       | NULL       |   NULL | 20         | product 20 |
| NULL       | NULL       |   NULL | 22         | product 22 |
+------------+------------+--------+------------+------------+
7 rows in set (0.01 sec)
Just use this:
SELECT bb.product_id as product_id, bb.short_name as short_name FROM (select ref_id, product_id, short_name from ap_company_product where ref_id=2) aa RIGHT OUTER JOIN ap_company_product_list bb ON aa.product_id = bb.product_id where aa.product_id is NULL;
BTW, I would prefer this one:
SELECT product_id, short_name FROM ap_company_product_list WHERE product_id NOT IN (SELECT product_id FROM ap_company_product);

Getting specific values from many-to-many relationships

My database looks like this, I have client accounts which are assigned to specific profiles, and I have profiles which are assigned to specific categories, like in this schema:
| categories | | profiles | | categories_map |
--------------- ------------- ----------------------------
| ID | name | | ID | name | | ID | profile_id | cat_id |
--------------- ------------- ----------------------------
| 1 | cat1 | | 1 | p1 | | 1 | 1 | 1 |
| 2 | cat2 | | 2 | p2 | | 2 | 2 | 1 |
| 3 | cat3 | | 3 | p3 | | 3 | 3 | 1 |
| 4 | p4 | | 4 | 1 | 2 |
| 5 | 3 | 2 |
| 6 | 4 | 3 |
| profiles_map |
-----------------------------
| ID | profile_id | acc_id |
-----------------------------
| 1 | 1 | 1 |
| 2 | 3 | 1 |
| 3 | 4 | 1 |
I need to get categories assigned to accounts - which means when I want to get categories for acc_id = 1, I should get categories with ID 2 and 3 ( category with ID 2 doesn't fit because it contains profile with ID 2 which isn't assigned to this account). I tried this query but it doesn't work
select cats.id from profiles_map map
right join categories_map catm on catm.profile_id = map.profile_id
right join categories cats on cats.id = catm.cat_id
where catm.profile_id in (select profile_id from profiles_map where acc_id = 1)
and map.acc_id = 1 group by cats.id;
Could anybody help me with this question?
Can you try this one?
SELECT DISTINCT C.ID
FROM profiles_map PM
INNER JOIN categories_map CM ON CM.profile_id = PM.profile_id
INNER JOIN categories C ON C.ID = CM.cat_id
WHERE PM.acc_id= 1
If you want to get only category id, Please try following query:
SELECT DISTINCT cm.cat_id FROM categories_map cm
WHERE cm.profile_id in
(SELECT profile_id FROM profiles_map WHERE acc_id = 1)
Or if want to get category name and id then , use following query:
SELECT cat.id,cat.name FROM categories cm
WHERE cat.id in (SELECT DISTINCT cm.cat_id FROM categories_map cm
WHERE cm.profile_id in
(SELECT pm.profile_id FROM profiles_map pm WHERE pm.acc_id = 1))

MySQL - Similar headings - multiple tables

I have 2 tables, one containing the main information with a second table containing the same fields plus additional. The second table would have data which should overwrite the first table when queried. The fields are bigger, I've just shortened them down to show here.
First table (products):
+----+--------------+--------------+---------------+--------------------------+
| id | manufacturer | product_name | product_title | product_description |
+----+--------------+--------------+---------------+--------------------------+
| 1 | testingA | productA | productTitleA | main product description |
| 2 | testingA | productB | productTitleB | main product description |
| 3 | testingA | productC | productTitleC | main product description |
+----+--------------+--------------+---------------+--------------------------+
Second Table (products_secondary)
+----+------------+--------------+--------------+---------------+----------------------+---------+
| id | product_id | manufacturer | product_name | product_title | product_description | context |
+----+------------+--------------+--------------+---------------+----------------------+---------+
| 1 | 1 | (null) | (null) | (null) | new description here | test |
+----+------------+--------------+--------------+---------------+----------------------+---------+
My aim is to select the values from the second table where context = text
Expected result:
+----+--------------+--------------+---------------+--------------------------+
| id | manufacturer | product_name | product_title | product_description |
+----+--------------+--------------+---------------+--------------------------+
| 1 | testingA | productA | productTitleA | new description here |
| 2 | testingA | productB | productTitleB | main product description |
| 3 | testingA | productC | productTitleC | main product description |
+----+--------------+--------------+---------------+--------------------------+
The query which I know works is the following:
SELECT IFNULL(`products_secondary`.`product_description` , `products`.`product_description`) AS `product_description`
FROM `products`
LEFT JOIN `products_secondary` ON `products_secondary`.`product_id` = `products`.`id` AND `products_secondary`.`context` = 'test'
I'm sure there is an easier way to do this than having to supply IFNULL() for each field. Here is an SQL Fiddle: http://sqlfiddle.com/#!9/6985b/1
Thanks.
Use this query
SELECT a.id, a.manufacturer, a.product_name, a.product_title, IF(b.context IS NOT NULL, b.product_description, a.product_description) as product_description
FROM
products a
LEFT OUTER JOIN products_secondary b ON a.id=b.product_id

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.