How can I get a purchase check in MySQL - mysql

I am not able to figure out how I can get the following result with one MySQL Query:
I have two tables:
shop_items
| id | description | price | active |
+----+-------------+-------+--------+
| 1 | product_1 | 5 | 1 |
+----+-------------+-------+--------+
| 2 | product_2 | 10 | 1 |
+----+-------------+-------+--------+
| 3 | product_3 | 15 | 0 |
+----+-------------+-------+--------+
inventory_items (the shop_items a user purchased)
| id | item_id | user_id | active |
+----+---------+---------+--------+
| 1 | 2 | 1 | 1 |
+----+---------+---------+--------+
| 2 | 1 | 1 | 0 |
+----+---------+---------+--------+
I want to see all shop_items where active = 1 including a row called purchased = 0 or 1 based on inventory_items -> matching user_id (where user_id = something) and active = 1
Example output based on the data from above tables -> where user_id = 1:
| item_id | price | description | purchased |
+---------+-------+-------------+-----------+
| 1 | 5 | product_1 | 0 |
+---------+-------+-------------+-----------+
| 2 | 10 | product_2 | 1 |
+---------+-------+-------------+-----------+
What query do I need for this output?
Please note: I only need the result from ONE user_id which I can change within the query :)

Test
SELECT shop_items.*, COALESCE(inventory_items.active, 0) purchased
FROM shop_items
LEFT JOIN inventory_items ON shop_items.id = inventory_items.item_id
AND user_id = 1
WHERE shop_items.active = 1

Related

Combine two tables into one table without using join and without the result creating cartesian product

How do I make a table of results of two different ratings without creating a cartesian product. diffrating and hostrating are two different types of user ratings. The users are rating the spots which are are labeled with spotId. userdiffrating and userhostrating are linking the users ratings to the correct spots. I am trying to get all the ratings for a specific spot in a table. Sample data and the expected output are bellow.
Table contents:
userdiffrating:
-------------------------------
| RatingId | userId | spotId |
-------------------------------
| 1 | 1 | 1 |
-------------------------------
| 2 | 2 | 1 |
-------------------------------
| 3 | 1 | 2 |
-------------------------------
diffrating:
----------------------
| RatingId | userId |
----------------------
| 1 | 5 |
----------------------
| 2 | 2 |
----------------------
| 3 | 4 |
----------------------
userhostrating:
-------------------------------
| RatingId | userId | spotId |
-------------------------------
| 1 | 1 | 1 |
-------------------------------
| 2 | 2 | 1 |
-------------------------------
| 3 | 1 | 1 |
-------------------------------
hostrating:
----------------------
| RatingId | userId |
----------------------
| 1 | 1 |
----------------------
| 2 | 3 |
----------------------
| 3 | 4 |
----------------------
This is what I originally tried but this creates a cartesian product:
SELECT D.rating diffrating, H.rating hostrating FROM diffrating D
JOIN userdiffrating UD ON D.ratingId = UD.ratingId
JOIN userhostrating UH ON UD.spotId = UH.spotId
JOIN hostrating H ON UH.ratingId = H.ratingId WHERE UD.spotId = 1
Result from first query (cartesian product):
-------------------------
| diffrating| hostrating|
-------------------------
| 5 | 1 |
-------------------------
| 5 | 3 |
-------------------------
| 5 | 4 |
-------------------------
| 2 | 1 |
-------------------------
| 2 | 3 |
-------------------------
| 2 | 4 |
-------------------------
I tried this next query but I cant use a select statement that has more than one row as a subquery:
SELECT D.rating AS diffrating, H.rating AS hostrating
FROM diffrating D, hostrating H
WHERE D.ratingId = (SELECT ratingId FROM userdiffrating UD WHERE UD.spotId = 1)
AND H.ratingId = (SELECT ratingId FROM userhostrating UH WHERE UH.spotId = 1)
This is the expected result (all diff and host ratings for spotId = 1):
-------------------------
| diffrating| hostrating|
-------------------------
| 5 | 1 |
-------------------------
| 2 | 3 |
-------------------------
| NULL | 4 |
-------------------------
This is the database:
Is this possible and how would this be done?
Thanks
I think what you are trying to code is this
SELECT dr.rating diffratingval, hr.rating hostratingval
FROM diffrating dr
JOIN userdiffrating udr
ON dr.ratingid = udr.ratingid
JOIN userhostrating uhr
ON udr.spotid = uhr.spotid
JOIN hostrating hr
ON uhr.ratingid = hr.ratingid
AND dr.ratingid = hr.ratingid
WHERE udr.spotid = 60;

MySQL nested Select statement with subquery

I am struggeling with a database query for 2 Hours now.
There is the following database structure:
article table
+---------------+-------------+
| id | ordernumber |
+---------------+-------------+
| 1 | 3243 |
| 2 | 3344 |
| 3 | 3423 |
| 4 | 7687 |
+---------------+-------------+
variant table
+----+-----------+-------+-------+
| id | articleId | stock | price |
+----+-----------+-------+-------+
| 1 | 1 | 3 | 10,99 |
| 2 | 1 | 0 | 10,99 |
| 3 | 1 | 1 | 10,99 |
| 4 | 2 | 0 | 11,99 |
| 5 | 2 | 0 | 11,99 |
| 6 | 2 | 1 | 11,99 |
+----+-----------+-------+-------+
I want to get all Articles where all but one variant have 0 stock.
Is this even possible with a plain sql statement? I tried with a subquery, but without success, since the subquery gets executed first and I would need to pass values from the current record of the resultset of the outer query.
Any help is much appreciated.
Edit:
Expected Result:
+----+-------------+
| id | ordernumber |
+----+-------------+
| 2 | 3344 |
+----+-------------+
If you want the full information for the variant:
select v.*
from variants v
where v.stock > 0 and
not exists (select 1
from variants v2
where v2.articleID = v.articleID and
v2.stock > 0 and
v2.id <> v.id
);
Note: this assumes that the duplicated "5" is a typo and that the ids really are unique in the table.
This can be done using group by and having.
select articleID
from variants
group by articleID
having count(*) - 1 = count(case when stock = 0 then 1 end)

Update records based on date on MySQL using joins

I have all those tables above.
car_model_tbl
-----------------------------
id | car_model_name|status |
-----------------------------
1 | seria_1 | 1 |
-----------------------------
2 | golf_4 | 1 |
-----------------------------
3 | C_Class | 1 |
-----------------------------
4 | golf_5 | 1 |
-----------------------------
5 | seria_2 | 0 |
-----------------------------
car_manufacturer_tbl
-------------------------
id |car_manufactu_name |
-------------------------
1 | bmw |
-------------------------
2 | volkswagen |
-------------------------
3 | mercedes |
-------------------------
car_service_tbl
---------------------------------
id | model_id| service_date |
---------------------------------
1 | 1 | 2018-03-10 |
---------------------------------
2 | 2 | 2018-02-10 |
---------------------------------
3 | 1 | 2018-01-10 |
---------------------------------
4 | 1 | 2017-12-10 |
---------------------------------
5 | 2 | 2017-12-10 |
---------------------------------
6 | 3 | 2018-02-10 |
---------------------------------
7 | 2 | 2018-01-10 |
---------------------------------
9 | 4 | 2018-03-10 |
---------------------------------
10 | 4 | 2018-02-10 |
---------------------------------
11 | 5 | 2018-02-10 |
---------------------------------
car_model_manufacturer_relation
-------------------------------------------------
id | model_id | manufactu_id| service_status |
-------------------------------------------------
1 | 1 | 1 | 1 |
-------------------------------------------------
2 | 5 | 1 | 1 |
-------------------------------------------------
3 | 2 | 2 | 1 |
-------------------------------------------------
4 | 4 | 1 | 1 |
-------------------------------------------------
5 | 2 | 2 | 1 |
-------------------------------------------------
6 | 3 | 3 | 1 |
-------------------------------------------------
I need to update car_model_manufacturer_relation.service_status = '0'
where car_service_tbl.service_date < "2018-03-01".
In this case car_model_manufacturer_relation.service_status of models 2, 3 and 5 should be set to '0' because every car_service_tbl.service_date for these models is smaller than "2018-03-01".
However, for models 1 and 4 car_model_manufacturer_relation.service_status should stay '1' because even that they have records smaller than "2018-03-01" they also have bigger dates ex. "2018-03-10".
I am trying to create a query for this but until now without success.
You'll need to nest a grouped query, to get the MAX date per model, and update from that.
update car_model_manufacturer_relation as cmmr,
(select model_id, max(service_date) as check_date
from car_service_tbl
group by model_id) as cst
set cmmr.service_status = '0'
where cmmr.model_id = cst.model_id
and cst.check_date < "2018-03-01"
Where you're using more than one table and the table names include underscores, I try and alias the tables to make the code a little shorter and easier on the eye, hence the use of cmmr and cst as table aliases.
The MAX date has also been renamed for clarity as check_date. You can of course name this anything you wish.
With sub query:
UPDATE car_model_manufacturer_relation c
LEFT join (SELECT model_id, service_date FROM car_service_tbl ORDER BY service_date DESC LIMIT 1) as s ON s.model_id = c.model_id
SET service_status=0
WHERE c.service_date < "2018-03-01"
#tyro - be careful with your solution, as a LEFT JOIN would update the service status to 0 when there wasn't a service date within the car_service_tbl. You would need to use a full join, rather than just the LEFT JOIN as you suggested in order to update the records correctly I feel.

Getting rows which have a certain revision_status

I am using mysql and I have two tables:
Product Table:
| id | name | prices | revision_id |
|----|-----------|--------|-------------|
| 1 | Produkt 1 | 10 | 1 |
| 2 | Produkt 1 | 4 | 2 |
| 3 | Produkt 1 | 2 | 3 |
| 4 | Product 2 | 42 | 4 |
| 5 | Produkt 2 | 43 | 5 |
| 6 | Produkt 3 | 78 | 6 |
Each product has had price changes. That is why the name is still the same, but the products have a different price.
Revisions Table:
| id | revision_status |
|----|-----------------|
| 1 | 1 |
| 2 | 0 |
| 3 | 0 |
| 4 | 1 |
| 5 | 0 |
| 6 | 1 |
Inside the revision table, 0 indicates an open change, not approved change. 1 indicates - closed - an approved change.
Expected Result:
| id | name | prices | revision_id | revision_status |
|----|-----------|--------|-------------|-----------------|
| 1 | Produkt 1 | 10 | 1 | 1 |
| 2 | Produkt 1 | 4 | 2 | 0 |
| 3 | Produkt 1 | 2 | 3 | 0 |
| 4 | Product 2 | 42 | 4 | 1 |
| 5 | Produkt 2 | 43 | 5 | 0 |
Basically I want all products that have revisions - a revision_status of 0 on it, to see which products actually have revisions.
For example.: Product 3 does not have any price changes, so it should not appear in the final result.
I tried the following:
select *
from product
JOIN revisions
on product.revisions_id = revisions.id
ORDER
BY product.name
However, I still get Product 3 in my table and I am not sure how to get all products that have a revision_status of 0 on it.
I highly appreciate your replies!
In my interpretation you are looking for the products which have more than one revision. Filtering only on revision_status = 0 would not produce your expected result. The following query may answer your question (looking for those products which have more than 1 revision):
SELECT *
FROM product AS p
INNER JOIN revisions AS r ON p.revision_id = r.id
WHERE p.name IN (
SELECT p.name
FROM product AS p
INNER JOIN revisions AS r ON p.revision_id = r.id
GROUP BY p.name
HAVING COUNT(r.revision_status) > 1)
ORDER BY p.name
This would produce your expected result. See example at sqlfiddle.

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.