Fetching data from mapping table - mysql

I have a table structure like this:
bundle_id|service_id
8|1
8|2
8|3
9|1
9|4
10|1
10|2
10|3
10|4
10|5
Now, I want to query the table to fetch the optimal bundle which has particular set of services.
E.g. If I want to fetch the bundle having services 1,2 and 3 then it should return 8 (and not 10).
Can anyone please help me with the query?

Try this:
SELECT bundle_id
FROM mappings
WHERE service_id IN (1, 2, 3)
GROUP BY bundle_id HAVING COUNT(DISTINCT service_id) = 3
ORDER BY bundle_id LIMIT 1;
EDIT
SELECT m.bundle_id
FROM mappings m
INNER JOIN (SELECT bundle_id, COUNT(DISTINCT service_id) serviceIdCnt
FROM mappings GROUP BY bundle_id
) A ON m.bundle_id = A.bundle_id
WHERE m.service_id IN (1, 2, 3)
GROUP BY m.bundle_id HAVING COUNT(DISTINCT m.service_id) = 3
ORDER BY A.serviceIdCnt LIMIT 1;
Check the SQL FIDDLE DEMO
OUTPUT
| BUNDLE_ID |
|-----------|
| 8 |

Related

How to transform this SQL query to query with GROUP BY and HAVING?

There're products, products_filters, filters_values tables in DB.
products_filters references to products table via product_id column which is a foreign key.
Also products_filters references to filters_values table via filter_value_id column which is also a foreign key
When user selects filters, SQL query which extracts all ids of suitable products is formed.
For example, chosen filters are:
Sex: Male, Female
Brand: Brand1, Brand2, Brand3
How it should work:
It needs to select all products which have filter Sex set to Male OR Female AND filter Brand set to Brand1 OR Brand2 OR Brand3. But products having matching only in one of the chosen filter category either Sex or Brand, must not be selected. It necessiraly to have matching in all selected categories.
I think SQL should look like this:
SELECT product_id FROM products_filters WHERE
(filter_value_id = 1 OR filter_value_id = 2)
AND
(filter_value_id = 3 OR filter_value_id = 4 OR filter_value_id = 5)
Where 1 is Male, 2 is Female, 3 is Brand1, 4 is Brand2, 5 is Brand3.
But this query doesn't work.
In my previous question I was answered that GROUP BY and HAVING may help.
Q: How can I transform SQL above with GROUP BY and HAVING?
Given
drop table if exists t;
create table t(id int ,gender varchar(1), brand varchar(1));
insert into t values
(1,'m',1),(1,'f',2),(1,'m',3),(2,'f',1),(2,'f',2),(2,'f',3),
(3,'m',1),(4,'f',2);
Correlated sub queries with distinct
select distinct id
from t
where
(select count(distinct gender) from t t1 where gender in('m','f') and t1.id = t.id) = 2 and
(select count(distinct brand) from t t1 where brand in(1,2,3) and t1.id = t.id) = 3
;
+------+
| id |
+------+
| 1 |
+------+
1 row in set (0.002 sec)
SELECT product_id
FROM products_filters AS f1
JOIN products_filters AS f2 USING(product_id)
WHERE f1.filter_value_id IN (1,2)
AND f2.filter_value_id IN (3,4,5)
(I don't think GROUP BY...HAVING COUNT(*) = 2 is reasonable for this case.)
(The EAV schema design is a pain to deal with.)

SQL Query to fetch results from a table

Many thanks for any help.
I have a following table consisting of 2 columns service_id and artist_id. Primary Key is (service_id, artist_id). I want to retrieve all the artists for a set of service ids.
Sample Table :
service_id artists_id
5 9
6 9
5 10
1 9
5 1
6 1
6 7
1 10
I tried this and it is not working as it gives all the artists who give either service id 5 or service id 6 or service id 1.
SELECT artists_id FROM `service_schedule` WHERE service_id IN (5, 6, 1)
I want artists who give service id 5 And Service Id 6 And Service Id 1. For the above sample table only artist 9 gives all the 3 services in the set (5,6,1).
So if i want to retrieve all artists who can give services with id 5 and 6 and 1. How do i write a SQL query?
Once try this,
select artist_id from table where service_id IN (5, 6) AND
artist_id IN (select artist_id from table group by artist_id having count(*) > 1);
I am fetching all artist_id which has count greater than 1 and must be service_id with 5 and 6.
I hope this will help.
EDIT
select artists_id from Sample where service_id IN (5, 6, 1)
AND
artists_id IN (select artists_id from Sample group by artists_id
having count(*) > 2) group by artists_id;
You can get, service_ids like, 5,6 or 5,6,1, you just need to take count of service ids and then keep that value - 1 in place of 2
EDIT
select * from Sample where service_id IN (5, 6, 1, 8) and is_available = 1
AND
artists_id IN (select artists_id from Sample where is_available = 1 group by artists_id
having count(*) > 3) group by artists_id;
You have to use a group by query:
select
artist_id
from
service_schedule
group by
artist_id
having
sum(service_id in (5,6,1))=3
service_id in (5,6,1) will be evaluated as 1 when the condition is true, and you want to check if for every artist_id the sum is 3 (all three services are given - arist_id and service_id is unique so no need to check about duplicated rows here)
The question is not much clear to me but is following solution works for you if I understood it clearly.
select s1.service_id, s1. artists_id
from Sample s1 inner join Sample s2
on s1.artists_id = s2.artists_id
where s2.service_id = 5 and s1.service_id = 6
union
select s1.service_id, s1. artists_id
from Sample s1 inner join Sample s2
on s1.artists_id = s2.artists_id
where s2.service_id = 6 and s1.service_id = 5
Sample Output - http://sqlfiddle.com/#!9/7b3a5/16
Try this:
SELECT DISTINCT(s1.artists_id) FROM service_schedule s1
JOIN service_schedule s2 ON s2.service_id = s1.service_id AND s2.artists_id <> s1.artists_id
WHERE s1.service_id IN (5, 6, 1)

selecting multiple max values

i have a table like this on a mysql database:
id | item
-----------
1 | 2
2 | 2
3 | 4
4 | 5
5 | 8
6 | 8
7 | 8
i want the result to be 3 record with the highest Item value
select max(item) returns only 1 value
how can i select multiple max values?
thank you
You can use a derived table to get the maximum value and join it back to the original table to see all rows corresponding to it.
select t.id, t.item
from tablename t
join (select max(item) as mxitem from tablename) x
on x.mxitem = t.item
Edit:
select t.co_travelers_id, t.booking_id, t.accounts_id
from a_co_travelers t
join (select accounts_id, max(booking_id) as mxitem
from a_co_travelers
group by accounts_id) x
on x.mxitem = t.booking_id and t.accounts_id = x.accounts_id
If you use an 'aggregate function' without GROUP BY only one row will be returned.
You may use GROUP BY , with aggregate functions.
Here is SQLFiddle Demo
SELECT id,max(item) AS item
FROM table_name
GROUP BY id
ORDER BY item DESC
LIMIT 3
Hope this helps.
There is the graphical explanation.
There is script mysql (low abstraction level, no inner join or sth)
select * from ocena, uczen where ocena.ocena = (SELECT MAX(ocena.ocena) FROM ocena WHERE ocena.przedmiot_id="4" and ocena.uczen_id="1") and ocena.uczen_id=uczen.id and ocena.przedmiot_id="4" and uczen_id="1"

Select result that match ALL array values

I'm trying to solve this for quite a moment now and I don't seem to be able to do it by myself.
I'd like to store OPTIONS linked to IDs, and when needed, get the results that match all wanted OPTIONS. I thought about doing it this way:
ID | OPTION
aaa | 1
aaa | 2
aaa | 3
bbb | 1
bbb | 2
ccc | 1
ccc | 2
ccc | 5
ccc | 7
Where ID and OPTION are FOREIGN KEYS.
The final request would look like
options_wanted(1,2,5,7)
SELECT * FROM main_table
WHERE crit1=...
AND crit2=...
AND (ALL OPTIONS ARE FOUND IN options TABLE)
Can I make it work or should I change the implementation?
What do you suggest me?
EDIT:
Thanks to https://stackoverflow.com/a/7505147/2512108, I almost found what I want.
His query works but the last column only gives the 1st option alone. Is there a way to make it return ALL the options AVAILABLE (not only the wanted ones) ?
Answer:
select item_id, group_concat(option_id order by option_id asc) options
from options
where option_id in (1, 2, 3)
group by item_id
having count(option_id) = 3
Fiddle: http://sqlfiddle.com/#!9/04f69/3
I'll leave the joining to your other table up to you, as well as the other criteria since the table schema isn't really explicitly mentioned.
EDIT
No I won't, I hate half an answer.
select item_id, group_concat(option_id order by option_id asc) options
from main_table m
inner join options o
on m.id = o.item_id
where option_id in (1, 2, 3)
AND crit1 = 2
AND crit2 = 3
group by item_id
having count(option_id) = 3
Updated fiddle: http://sqlfiddle.com/#!9/45bee/1
And if you want it to return ALL options available to an item that has at minimum all of the REQUIRED options, your query is this:
select o.item_id, group_concat(o.option_id) options
from options o
inner join (
select item_id
from main_table m
inner join options o
on m.id = o.item_id
where option_id in (1, 2, 3)
AND crit1 = 2
AND crit2 = 3
group by item_id
having count(option_id) = 3
With a final fiddle here: http://sqlfiddle.com/#!9/d60b3/1
As you state:
'Criterias and options are different'
and
'All options are found in options table'
I assume you have two tables; Namely main_table and options_table
SELECT id, option
FROM main_table
WHERE crit1='value1'
and crit2 = 'value2'
and id IN(
SELECT id
FROM main_table
WHERE option IN (select options from options_table)
GROUP BY id
HAVING COUNT(*) = (select count(options) from options_table)
Find similar question over here:
SQL selecting rows where one column's value is common across another criteria column
MADE IT ! Finally, thanks to you
SELECT main_table.*, (SELECT GROUP_CONCAT(feature_id) FROM featuring WHERE main_id = main_table.id) options FROM main_table
RIGHT JOIN featuring
ON main_table.id = featuring.main_id
WHERE featuring.feature_id IN (WANTEDOPTIONS)
GROUP BY main_table.id
HAVING COUNT(DISTINCT featuring.feature_id) = NUMBEROFWANTEDOPTIONS
It gives me all the informations on the main and ALL the AVAILABLE options in featuring.
Thanks again.

MySQL query by string and limit

I have a table "articles" that has a column "company" which has list of companies or an article type - crappy I know, but it's not my DB. :) Let's say there are n articles for each type. I need to select the first article (based on year, or any other criteria) that is of the type. Something like this:
select * from details where (company = 'aaa' or company = 'bbb' or ...)
I know what the types are, so they can be hardcoded. I need to limit only the first article for each type. Thanks!
EDIT
given the sample data:
id company copy issue
------------------------
1 apple 'abc' NULL
2 bmw 'abc' NULL
3 ibm 'abc' NULL
4 news 'abc' 2
5 news 'abc' 3
6 seagate 'abc' NULL
7 events 'abc' 1
8 features 'abc' 5
9 samsung 'abc' NULL
I need rows 4, 7, 8.
EDIT2
Sorry if I wasn't clear. Essentially the table contains two different types of data. One is company info, and one is article info. Basically I need to do this:
select * from articles where company = "news" order by issue limit 1;
select * from articles where company = "events" order by issue limit 1;
select * from articles where company = "features" order by issue limit 1;
but with a single query.
Something like this perhaps
select * from details d
where company in ('news', 'events', 'features')
and not exists (
select 1 from details d_
where d_.company = d.company
and d_.id < d.id -- provide your sortable criteria here
)
Example here - http://sqlfiddle.com/#!2/bb8f2/6
This query:
select t1.* from t t1
left join t t2
on t1.company = t2.company and t1.id > t2.id
where t2.id is null and t1.company in ('news', 'events', 'features')
will return:
+----+----------+------+
| ID | COMPANY | COPY |
+----+----------+------+
| 4 | news | abc |
| 7 | events | abc |
| 8 | features | abc |
+----+----------+------+
Is that what you're looking for?
Note: When you say the first article I assume the order is provided by the ID field
You can play with it here
Edit:
After your edit, the query is almost the same, just change the ordering field to issue instead of id:
select t1.* from t t1
left join t t2
on t1.company = t2.company and t1.issue > t2.issue
where t2.id is null and t1.company in ('news', 'events', 'features')
Assuming "company" and "publication_date" are columns in table "details", then something like:
select *
from details
where company in ('aaa', 'bbb', ...)
and publication_date between '2012-02-01' and '2012-03-01'
order by publication_date desc
limit 1
You are trying to do a group by.
Next you want to order by the issue column.
Then you only need the first row.
I used group concat to create a list,
then extract the first string in the ordered list group_concat(copy order by copy).
select
id,
company,
substring(
group_concat(copy order by copy),
1,
case when substring_index(group_concat(copy order by copy),',',1) > 1
then substring_index(group_concat(copy order by copy),',',1)-1
else 1 end
) as copy
from details d
where company in ('news', 'events', 'features')
group by company
*the list group_concat(copy order by copy) cannot be too long though, which depends on your data.