mysql logic for results spanning multiple rows - mysql

I have a product search query which returns rows similar to this:
| id | name | search tag |
| 1 | cat | furry |
| 1 | cat | ginger |
| 2 | dog | furry |
What I need to do is be able to get the single row that matches more than one search tag - for this example, a search for furry AND ginger returns no rows because there is only one search tag per row (this is because to get these results I'm using an INNER JOIN).
My question is, what do I need to do to be able to test for furry AND ginger in the query and return the cat but not the dog?
I'm doing this in PHP.
UPDATE
In order to cope with users putting in duplicate tags, a simple fix to the HAVING condition will return rows that have duplicate tags rather than ignoring them:
mysql> select id, name, group_concat(tag) as tags,
count(tag) as cnt from animals where tag in ('furry', 'ginger')
group by id HAVING cnt>=2;

You could first select the rows that have the desired tags, then group them by animal ID. Count how many results you get per animal and if it's the same as the number of tags you wanted you got a match.
mysql> select id, name, group_concat(tag) as tags,
count(tag) as cnt from animals where tag in ('furry', 'ginger')
group by id HAVING cnt=2;
+----+------+--------------+-----+
| id | name | tags | cnt |
+----+------+--------------+-----+
| 1 | cat | furry,ginger | 2 |
+----+------+--------------+-----+

Related

MySQL: select all rows where just the name is distinct [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 4 years ago.
I'm currently trying to select unique entries in only the name column. I have tried using this query but it will not return prices that are the same as well. I've tried other variations with no success either.
SELECT DISTINCT name, price from table;
Here's the table I'm working with:
+----+-------------------+
| id | name | price |
+----+-----------+-------+
| 1 | Henry | 20 |
| 2 | Henry | 30 |
| 3 | Robert | 20 |
| 4 | Joshua | 10 |
| 5 | Alexander | 30 |
+----+-----------+-------+
The output that I'm seeking is:
+----+-------------------+
| id | name | price |
+----+-----------+-------+
| 1 | Henry | 20 |
| 3 | Robert | 20 |
| 4 | Joshua | 10 |
| 5 | Alexander | 30 |
+----+-----------+-------+
The desired output as you can tell only removed the duplicate name and none of the prices. Is there something I can add to my query above to only select unique entries in the name column? Any help is really appreciated as I have tried to find a solution on here, Google, DuckDuckGo, etc. with no luck.
From your sample data, this should work.
SELECT MIN(Id) AS Id, name, MIN(price) AS price
FROM table
GROUP BY name;
This is what GROUP BY is for:
SELECT * FROM `table` GROUP BY `name`
Usually people run into trouble because they will now get an arbitrarily-chosen row when more than one matches for a given name — you have to use aggregate functions to pick a specific one, e.g. "the one with the maximum price".
But in your case, since you don't seem to care which row is returned, this is perfect as-is.
So you want to select distinct list of rows AND then select that given entire row from the table? Try this query where temporary query is just a list of uniqueid then that row is linked back to the table.
Select n.*
From nameprices n
Join (Select MIN(id) as id
From nameprices
Group by name
Order By id) aTemp On (aTemp.id=n.id);
This is a common problem in SQL queries where we want to use that given fully row data but filter was using a distinct/groupby formula.

SQL: many-to-many relationship and the 'ALL' clause

I have a table products and a table locations which are linked together in a many-to-many relationship with a table products_locations. Now a client can select a set of products, and I want to run a query that selects only the locations, where ALL of the selected products are available.
This seemed pretty straight forward at first, but I see myself being quite baffled by how to achieve this. I initially thought I could get all the correct location-ids with something like
SELECT location_id
FROM products_locations
WHERE product_id = ALL [the user selected product ids]
But on second thought that does not appear to make sense either (the structure of products_locations is quite simply [product_id, location_id].
Any suggestion on how to structure such a query would be appreciated. I feel like I am overlooking something basic..
EDIT: I am using mysql syntax/dialect
Quick sample: Given the following tables
| products | | locations | | products_locations |
| id | name | | id | name | | product_id | location_id |
|------------| |-----------| |--------------------------|
| 1 | prod1 | | 1 | locA | | 1 | 2 |
| 2 | prod2 | | 2 | locB | | 2 | 1 |
| 3 | prod3 | |-----------| | 2 | 2 |
|------------| | 3 | 1 |
|--------------------------|
If a user selects products 1 and 2, the query should return only location 2. If the user selects products 2 and 3, the query should return location 1. For 1, 2, and 3, no location would be valid, and for product 2, both locations would be valid.
I figured out a query that achieves what I need. Though it is not as clean as I had hoped, it seems to be a robust approach to what I'm trying to query:
SELECT t.location_id
FROM (SELECT location_id, COUNT(*) as n_hits
FROM products_locations
WHERE product_id IN [the user selected products]
GROUP BY location_id) t
WHERE n_hits = [the number of user selected products];
Explanation:
I create a temporary table t which contains every location_id that has at least one matching product in the user's selection, together with the number of times that location matches a product in the user's selection. This is achieved by grouping the query by location_id.
I select the location_id(s) from that temporary table t, where the number of hits is equal to the number of products the user had selected. If that number is lower, I know that at least one product did not match that location.

return multiple rows in subquery mysql

i am new in mysql and stuck on a query.i want all the records of one table and multiple tags from other table that match with one column of first table.
select A.*,(select B.tag from crm_tags B where tag_id in (A.tags)) from crm_stores A;
there are multiple stores in crm_stores and each store has multiple tags in crm_tags. i want all details of store and each tag of store in one query.
when i tried the above query, it generates error : subquery returns multiple rows. please help, how to solve that problem
The tags in crm_stores are like "2098,2063",means multiple tags are comma seprated. While crm_tags has seprated entries
Thanks in advance
this is my first table crm_stores
+----------+-------+--------+-----------+----------+-
| store_id | guest | budget | tags | discount |
+----------+-------+--------+-----------+----------+-
| 23 | 5 | 1000 | 2098,2063 | 50% |
+----------+-------+--------+-----------+----------+-
this is my second table crm_tags
+--------+--------------+
| tag_id | tag |
+--------+--------------+
| 2063 | Chinese |
| 2098 | North Indian |
+--------+--------------+
use a join:
SELECT A.*, B.tag
FROM crm_stores A
LEFT JOIN crm_tags B ON A.tags LIKE concat(concat('%',B.tag_id),'%')
new mysql fiddle:
http://sqlfiddle.com/#!9/dedeb1/7/0

MySQL get data from three tables using one id

I have three different tables, which have following structure:
Food
ID | title
---+----------
1 | sandwich
2 | spaghetti
Ingridients
ID | food_reference | type | location | bought
----+----------------+------+----------+----------
100 | 1 | ham | storeA | 11-1-2013
101 | 1 | jam | storeB | 11-1-2013
102 | 2 | tuna | storeB | 11-6-2013
Tags
ID | food_reference | tag
----+----------------+-----
1000| 1 | Tag
1001| 1 | Tag2
1002| 2 | fish
and using one select I want to get all information from these three tables (title,type,location,bought,tag) for one specific ID.
I have tried something like
SELECT food.*,ingridients.*,tags.* FROM food
JOIN ingridients
ON :id=ingridients.food_reference
JOIN tags
ON :id=tags.food_reference
WHERE id=:id
BUT this query returns for id=1 only one row from ingridients and tags even though there are two matching rows (ham and jam, Tag and Tag2). Could you tell me what am I doing wrong?
EDIT: I tried LolCoder's solution, but I still got only one result, even though in fiddle it seems to work. However I tried :
SELECT F.*, group_concat(I.type), group_concat(I.location),
group_concat(I.bought), group_concat(T.tag)
FROM feeds F
INNER JOIN ingridients I
ON :id = I.food_reference
INNER JOIN tags T
ON :id=T.food_reference
WHERE F.id=:id
This finds data from ALL matching rows, but several times, i.e. I get (for id=1)
sandwich,ham,ham,ham,jam,jam,jam,tag,tag,tag,tag2,tag2,tag2
EDIT2: magic happened and LolCoder's solution works, so thank you :-)
Try with this query:
SELECT food.*,ingridients.*,tags.* FROM food
JOIN ingridients
ON food.id=ingridients.food_reference
JOIN tags
ON food.id=tags.food_reference
WHERE food.id=1
Check SQLFIDDLE
use alias with joins as.column name

mysql query - conditional statements?

Here are my tables:
files
+----+--------+
| id | name |
+----+--------+
| 2 | file_1 |
| 3 | file_2 |
| 5 | file_3 |
+----+--------+
files_already_viewed
+----+---------+----------+------+
| id | file_id | category | user |
+----+---------+----------+------+
| 1 | 3 | 5 | 1 |
| 2 | 2 | 1 | 1 |
| 3 | 5 | 1 | 1 |
+----+---------+----------+------+
categories_files_join
+--------+---------+
| cat_id | file_id |
+--------+---------+
| 1 | 2 |
| 1 | 5 |
| 5 | 3 |
| 1 | 3 |
+--------+---------+
file_2 (which has an id of 3) has two categories associated with it, cat_id 5 and cat_id 1.
It has been viewed once by a user searching for files under the category 5.
But now the user is searching for files under the category 1.
I need a query that won't show file_2 under the "1" category until all the other files with a category id of 1 have been viewed first, since the user already viewed file_2. Basically putting file_2 at the end of the list.
Here is my query so far:
SELECT name FROM files
WHERE files.id NOT IN (
SELECT file_id FROM files_already_viewed
WHERE user='1')
ORDER BY most_viewed DESC
LIMIT 1
I order my search by the most popular viewed file. But i don't want to show files that have already been viewed regardless of category until all other files have been viewed with in that specific category.
Any help would be greatly appreciated. thanks!
Actually, your query will not show files already shown. What you want to do is to order those already shown files at the bottom. So, basically you'll have to sets of data: first, the data that matches the needed criteria and that the user has not been shown yet and then the data that matches the needed criteria but the user has been shown.
The way I'm handling the sets is by adding a customSort id for each set and then ordering by it. Now the short explanation is that the first group I get it by faking a MINUS operation with a left join: I get all the files that have not yet been seen. Then, the second group is a bit easier as it just needs to get all the files that have already been seen. So, the UNION of both sets in the customSort order would be the result you're looking for so now you just need to filter that result by the current query criteria (category = 1 in this case).
select file_id from (
select distinct cf1.cat_id, cf1.file_id, 1 as customSort
from CategoriesFiles1 cf1
left join FilesViewed1 fv1 on (fv1.file_id = cf1.file_id)
where (fv1.file_id is null)
union
select distinct cf2.cat_id, cf2.file_id, 2 as customSort
from CategoriesFiles2 cf2
join FilesViewed2 fv2 on (fv2.file_id = cf2.file_id)
) FinalResult
where (FinalResult.cat_id = 1)
order by customSort
Here is a link with the example. You can comment each data insert in files_already_viewed to see how, after viewing a file, the result changes. Besides, changing select file_id from to select * from will allow you to clearly see which set each row belongs to.
Let me know if this works.