Selecting items that have same values in the same column - mysql

I am facing a conundrum; not sure why -- is it because this late, or I am just stuck. My goal is to create a filter on the webpage, so I am trying to figure this out.
I have a list of products that I store with filters in the reference table product_filter.
The structure:
id | product_id | filter1_id | filter2_id
1 | 1 | 2 | 1 <---
2 | 1 | 4 | 3
3 | 1 | 5 | 1
4 | 2 | 2 | 1 <---
5 | 2 | 3 | 1
6 | 3 | 2 | 1 <---
7 | 3 | 3 | 4
I need to submit a list of products (for example 1,2,3) and get only those filter combinations, that are the same for all selected product id's. So the result needs to be
filter1_id | filter2_id
2 | 1
My problem is that my products might vary and I can't do a ton of self inner joins... so I am stuck... Any advise?

Here is one approach that you could try:
select filter1_id, filter2_id
from product_filter
group by filter1_id, filter2_id
having count(*)=(
select count(distinct product_id)
from product_filter
)
This will only return a list when a combination of filter1_id and filter2_id exists for every product_id. (Fiddle here.) Is that what you are after? Do you don't mention what should be returned if there isn't any combination that exists for all of the given product_id's - an empty result set?
It's not a self-join (or even a ton of them ;) ) but it will still be fairly expensive I imagine.

Related

Different price for additional members of the same group

I have a simple table like this:
group | name | price
1 | john |
2 | mike |
3 | paul |
1 | sean |
4 | jack |
2 | brad |
5 | mick |
1 | bill |
4 | chad |
I have two different price values where 100EUR is for a first member of a group and 50EUR is for all additional members of that same group.
Detailed explanation. If a group has only one member, that member gets a price of 100EUR. If a group has multiple members, the first member gets a price of 100EUR, and all additional members of that same group get a price of 50EUR. There can be unlimited number of groups that will be added additionally.
The result should be like this:
group | name | price
1 | john | 100
2 | mike | 100
3 | paul | 100
1 | sean | 50
4 | jack | 100
2 | brad | 50
5 | mick | 100
1 | bill | 50
4 | chad | 50
I'd need a query which would be able to INSERT/UPDATE all missing price fields whenever I manually run it.
Thank you in advance for looking into that matter.
After a lot of trial and error I found a perfect fully functional solution, based on daviid's clever method. The issue with mysql is that by it's structure won't update tables with select methods as subquery. However, self-join (join or inner join) methods can be used instead in this case. I also had to add auto-incremental id to that table, so the final table structure is:
id | group_id | name | price
1 | 1 | john |
2 | 2 | mike |
3 | 3 | paul |
4 | 1 | sean |
5 | 4 | jack |
6 | 2 | brad |
7 | 5 | mick |
8 | 1 | bill |
9 | 4 | chad |
---
SET SQL_SAFE_UPDATES=0;
UPDATE table_name
SET price = 50;
UPDATE table_name AS a
JOIN
( SELECT id
FROM table_name
GROUP BY group_id
HAVING COUNT(*) >= 1
) AS b
ON a.id = b.id
SET a.price = 100;
Thanks also to Cody and Barmar for usable hints...
A partial answer: you can GROUP BY your "group" field and tack on a HAVING COUNT(group) > 1 to determine if that group has more than 1 member.
That is, to see all groups with more than one member it would look like:
SELECT
group
FROM table
GROUP BY group
HAVING COUNT(group) > 1
That will just tell you which groups have multiple members. Without another way to ensure ordering you cannot tell which member is "first" in their group and thus should be priced at 100 and all others priced at 50.
The following queries are not tested and might contain syntax errors. But they are good enough to understand the principle. There are many possible ways to achieve your result.
Here is my take: I would make use of one query to UPDATE the price on every row and set it to 50 whether it is the first group member or not. >table_name<, of course, needs to be changed to the name of your mentioned table.
UPDATE >table_name<
SET price = 50;
Then I would take care of each individual group and the respective first member by running the following query. Adapt the query to each group by changing the >groupId<.
UPDATE >table_name<
SET price = 100
WHERE id = (
SELECT id
FROM >table_name<
WHERE group = >groupId<
ORDER BY id
LIMIT 1
);
Take a look a the nested query: It queries the table for all members of only one group, orders them in ascending order and only returns an id per member. By applying LIMIT to the query, the result will just be the first group member's id. The resulting id can then be used in the other query to update the price and set it to 100.
But be careful: If you insert/delete (new) members with an id that is not just counting up, this query might select a "new first member".

How can I get the last row of every stage of every job? [duplicate]

This question already has an answer here:
Mysql group by two columns and pick the maximum value of third column
(1 answer)
Closed 2 years ago.
I've been taking too long trying to solve this, I need to filter this table:
+----+-------+-------+
| id | jobID |stageID|
+----+-------+-------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 2 |
| 4 | 1 | 1 |
| 5 | 2 | 2 |
| 6 | 2 | 1 |
| 7 | 2 | 1 |
| 8 | 2 | 2 |
+----+-------+-------+
You see every job has many rows with different stages, I need to get the last row of every stage of every job.
For example, look at job 1. It has 4 rows, each one with a given stage. I'd need to get the last entry of a stage for that job, which means, rows 3 and 4.
So for the full table I need to get rows No. 3, 4, 7 and 8, like this
+----+-------+-------+
| id | jobID |stageID|
+----+-------+-------+
| 3 | 1 | 2 |
| 4 | 1 | 1 |
| 7 | 2 | 1 |
| 8 | 2 | 2 |
+----+-------+-------+
I think I'll go nuts. I try with GROUP_BY but it groups the stages without taking in count the jobs.
Can you help me?
This is simply:
select max(id) as id, jobID, stageID
from yourtable
group by jobID, stageID
If you need additional information selected, use that as a subselect:
select yourtable.id, yourtable.jobID, yourtable.stageID, yourtable.other
from (
select max(id) as id
from yourtable
group by jobID, stageID
) max_job_stage_ids
join yourtable using (id)
or use IN (I find this less helpful in visualizing the query plan, but some people prefer it):
select id, jobID, stageID, other
from yourtable
where id in (select max(id) from yourtable group by jobID, stageID)

SQL: Creating a new table for a query result grouping by repeated column values [duplicate]

This question already has answers here:
How can I return pivot table output in MySQL?
(10 answers)
Closed 5 years ago.
sorry for the weird question but I didn't know how to exactly ask this. Or if there are similar questions to look at, please let me know.
I have the following table in my database. Basically it stores the prices of an item at different stores. Every store will always have the same items, meaning if store_id 3, 4,... n values are later added, they will have the same item_ids
+----+---------+----------+---------+
| id | item_id | store_id | price |
+----+---------+----------+---------+
| 1 | 1 | 1 | 74.99|
| 2 | 2 | 1 | 25.99|
| 3 | 3 | 1 | 89.99|
| 4 | 1 | 2 | 69.99|
| 5 | 2 | 2 | 39.99|
| 6 | 3 | 2 | 95.99|
+----+---------+----------+---------+
My sql knowledge is pretty basic, but I was wondering if there is a way to get this result.
I need to add every store_id value as a column and then get the price value for the item_id at every store available and put that in a single row. Since there could be more values for store_id, every different value
+---------+-----------+-----------+
| item_id | price for | price for |
| | store_id 1| store_id 2|
+---------+-----------+-----------+
| 1 | 74.99 | 69.99|
| 2 | 25.99 | 39.99|
| 3 | 89.99 | 95.99|
+---------+-----------+-----------+
Thanks.
you could use a self join eg:
select a.item_id, a.price as price_store_1, b.price as price_store_2
from my_table a
inner join my_table b on a.item_id = b.item_1 and b.store_id = 2
where a.store_id = 1

SQL select values on multiple rows [duplicate]

This question already has answers here:
MYSQL Select from tables based on multiple rows
(3 answers)
Closed 6 years ago.
If I have a table similar to the following:
+-------------+------------+-----------------+
| instance_id | product_id | option_value_id |
+-------------+------------+-----------------+
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 1 | 1 | 3 |
| 2 | 1 | 1 |
| 2 | 1 | 3 |
| 2 | 1 | 4 |
+-------------+------------+-----------------+
How would I create a query to find that instance_id 2 represents an instance of product_id 1 that has option values 1,3 and 4?
For example, I'd like to be able to query the table something like as follows:
select instance_id from instances where product_id = 1 'having' option_value_id = 1 and option_value_id=3 and option_value_id=4 (obviously this won't actaully work) and get the result
+-------------+
| instance_id |
+-------------+
| 1 |
Any help or pointers much appreciated
select product_id
, group_concat(option_value_id)
from table
where instance_id = 1
group by instance_id;
Obviously won't get what you want if product_id changes within instance_id, i.e. you could have multiple product_ids per instance_id. I don't know the schema but the above should get you started.
would help to know what your desired output would be... but perhaps something like:
Select A.Instannce_ID, B.instance_ID, group_concat(Distinct option_value_ID SEPERATOR ',') as Option_Values
from Table A
INNER JOIn table B
on A.Product_ID = B.Product_ID
and A.Instance_ID < B.Instance_ID
WHERE Option_value_ID In (1,3,4)
GROUP BY A.Instannce_ID, B.instance_ID
HAVING Count(distinct option_value_ID) = 3
This does a self join on the product_ID ensuring that the instances don't match and we don't get two way matches, and reports the option values in 1 field.

MySQL Order by before group by in view (No Sub-Querys)

I realize this question has been asked quite a few times, however i haven't managed to find a working solution for my case.
Essentially my problem arises because MySQL Doesn't allow sub-querys in views.
I found a few workarounds but they don't seem to work.
In more detail...
My first table (competitions) stores a users competitions:
id_tournament | id_competition | id_user | result
-------------------------------------------------
1 | 1 | 1 | 10
1 | 1 | 2 | 30
1 | 2 | 1 | 20
1 | 2 | 3 | 50
1 | 3 | 2 | 90
1 | 3 | 3 | 100
1 | 3 | 4 | 85
In this example there are three competitions:
(
user1 vs. user2,
user1 vs. user3,
user2 vs. user3 vs. user4
)
My problem is that i need to define a view that gives me the winners in each competition.
Expected Result:
id_tournament | id_competition | id_winner
------------------------------------------
1 | 1 | 2
1 | 2 | 3
1 | 3 | 3
This can be solved with the query:
SELECT
id_tournament,
id_competition,
id_user as id_winner
FROM (
SELECT * FROM competitions ORDER BY result DESC
) x GROUP BY id_tournament, id_competition
This query however uses a subquery (not allowed in views), so my first solution was to define a 'helper view'as :
CREATE VIEW competitions_helper AS (
SELECT * FROM competitions ORDER BY result DESC
);
CREATE VIEW competition_winners AS (
SELECT
id_tournament,
id as id_competition,
id_user as winner
FROM competitions_helper GROUP BY id_tournament, id_competition
);
However this does not seem to give the correct result.
It's result will then be:
id_tournament | id_competition | id_winner
------------------------------------------
1 | 1 | 1
1 | 2 | 1
1 | 3 | 1
What i don't understand is why it works when i use Sub-querys and why it gives a different result with the exact same statement in a view.
Any help is appreciated, thanks alot.
This is due to the GROUP BY behaviour.
In this case, the server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate, which is probably not what you want.
I would solve the problem in this way:
CREATE VIEW competitions_helper AS (
SELECT id_tournament,
id_competition,
MAX(result) as winning_result
FROM competitions
GROUP BY id_tournament,
id_competition
);
CREATE VIEW competition_winners AS (
SELECT c.id_tournament,
c.id_competition,
c.id_user
FROM competitions c
INNER JOIN competitions_helper ch
ON ch.id_tournament = c.id_tournament
AND ch.id_competition = c.id_competition
AND ch.winning_result = c.result
);