Query table depending on id matching comma separated field - mysql

I have a table on my database that lists different sections to show. We only want to show the sections if the user ID is inside a column of the database called 'userAccess' which will have a comma seperated list of the users who can see it.
Example row
id section userAccess
1 editNews 1,13,15
So how can I query that out and say something like
SELECT `section` WHERE '$userID' is in `userAccess`
I tried LIKE %, but obviously that will return all 3 users if I do LIKE %1%.

SELECT `section` WHERE FIND_IN_SET('$userID', `userAccess`) != 0

You could consider moving to a groups based permissioning structure, then have a GroupAccess table, with an FK to your section and the group ID. Users would be assigned to a Group in a UserGroup link table. This provides you with a lot of flexibility (but more complexity).
Or failing that, simply have
id section userAccess
1 editNews 1
2 editNews 13
3 editNews 15

Another alternative:
SELECT `section`
FROM t
WHERE concat(',', '$userID', ',') like concat(',', `userAccess`, ',')
In other words, you can use like if you put the separator around the two strings. I offer this because it is more similar to the solution you tried and you can use the same approach in other databases.

Related

How to select comma-separated values from a field in one table joined to another table with a specific where condition?

I'm working on a mysql database select and cannot find a solution for this tricky problem.
There's one table "words" with id and names of objects (in this case possible objects in a picture).
words
ID object
house
tree
car
…
In the other table "pictures" all the information to a picture is saved. Besides to information to resolution, etc. there are especially informations on the objects in the picture. They are saved in the column objects by the ids from the table words like 1,5,122,345, etc.
Also the table pictures has a column "location", where the id of the place is written, where I took the picture.
pictures
location objectsinpicture ...
1 - 1,2,3,4
2 - 1,5,122,34
1 - 50,122,345
1 - 91,35,122,345
2 - 1,14,32
1 - 1,5,122,345
To tag new pictures of a particular place I want to become suggestions of already saved information. So I can create buttons in php to update the database instead of using a dropdown with multiple select.
What I have tried so far is the following:
SELECT words.id, words.object
FROM words, pictures
WHERE location = 2 AND FIND_IN_SET(words.id, pictures.objectsinpicture)
GROUP BY words.id
ORDER BY words.id
This nearly shows the expected values. But some information is missing. It doesn't show all the possible objects and I cannot find any reason for this.
What I want is for example all ids fo location 2 joined to the table words and to group double entries of objectsinpicture:
1,5,122,34
1,14,32
1,5,14,32,34,122
house
...
...
...
...
...
Maybe I need to use group_concat with comma separator. But this doesn't work, either. The problem seems to be where condition with the location.
I hope that anyone has an idea of solving this request.
Thanks in advance for any support!!!
This is a classic problem of denormalization causing problems.
What you need to do is store each object/picture association separately, in another table:
create table objectsinpicture (
picture_id int,
object_id int,
primary key (picture_id, object_id)
);
Instead of storing a comma-separated list, you would store one association per row in this table. It will grow to a large number of rows of course, but each row is just a pair of id's so the total size won't be too great.
Then you can query:
SELECT w.id, w.object
FROM pictures AS p
JOIN objectsinpicture AS o ON o.picture_id = p.id
JOIN words AS w ON o.object_id = w.id
WHERE p.location = 2;

SQL IN Clause only returning rows with first match in comma separated list of IDs

I have 5 users which have a column 'shop_access' (which is a list of shop IDs eg: 1,2,3,4)
I am trying to get all users from the DB which have a shop ID (eg. 2) in their shop_access
Current Query:
SELECT * FROM users WHERE '2' IN (shop_access)
BUT, it only returns users which have shop_access starting with the number 2.
E.g
User 1 manages shops 1,2,3
User 2 manages shops 2,4,5
User 3 manages shops 1,3,4
User 4 manages shops 2,3
The only one which will be returned when running the IN Clause is User 2 and User 4.
User 1 is ignored (which it shouldn't as it has number 2 in the list) as it does not start with the number 2.
I'm not in a position to currently go back and change the way this is set up, eg convert it to JSON and handle it with PHP first, so if someone can try to make this work without having to change the column data (shop_access) that would be ideal.
A portable solution is to use like:
where concat(',', shop, ',') like '%,2,%'
Or if the value to search for is given as a parameter:
where concat(',', shop, ',') like concat('%,', ?, ',%')
Depending on your database, there may be neater options available. In MuSQL:
where find_in_set('2', shop)
That said, I would highly recommend fixing your data model. Storing CSV data in a database defeats the purpose of a relational database in many ways. You should have a separate table to store the user/shop relations, which each tuple on a separate row. Recommended reading: Is storing a delimited list in a database column really that bad?.
Also, you might want to consider using REGEXP here for an option:
SELECT *
FROM users
WHERE shop_access REGEXP '[[:<:]]2[[:>:]]';
-- [[:<:]] and [[:>:]] are word boundaries
SELECT * FROM users WHERE (shop_access = 2) OR (shop_access LIKE "2,%" OR shop_access LIKE "%,2,%" OR shop_access LIKE "%,2")

Mysql Joining on IN statement

I have a two tables one for news items and one for images.
News Table Structure
ID - numeric
newstitle - text
newsdesc - text
newsimages - text
Image Table Structure
ID - numeric
medianame - text
So a common news record would be:
ID = 1, newstitle="asdasfa", newsdesc="afsdgsgs", newsimages="1,2"
And the image table record would be:
ID=1, medianame="image.jpg"
I am trying to create a select statement that reads the data from the news table but provides me with the image name from the image table instead of the IDs.
If there is anything else ive left out let me know
Thanks
You can use FIND_IN_SET function
Example:
SELECT `news`.`ID`, `news`.`newstitle`, `news`.`newsdesc`,
GROUP_CONCAT(`images`.`medianame` SEPARATOR ',') AS `newsimages`
FROM `news`
LEFT JOIN `images` ON FIND_IN_SET(`images`.`ID`,`news`.`newsimages`)
GROUP BY `news`.`ID`
P.S. if you have (or planning to have) a big amount of rows, then you must to normalize the data.
it seems to be simple
SELECT N.*, I.medianame FROM News_Table N left join Image_Table I
ON N.Id=I.Id
I have assumed that tables names are News_table and Image_table respectively and that one news piece may have more than one image.

mySQL: Is it possible to make a SELECT statement, when the value in the DB is seperated with commas?

MY DB entry for the field data in the table productinfo is this for example:
66523,665893,745896321
Now I want a SELECT statement which gives me the hole entry:
For example:
SELECT * from productinfo WHERE products="66523"
select * from productinfo where FIND_IN_SET ('66523', products);
fiddle
Try:
SELECT *
FROM productinfo
WHERE Concat(',', products, ',') LIKE '%,66523,%'
In this fiddle, you can check that the first three rows are returned because they contain the 66523 product, but not the 4th one, which contain a product number containing 66523.
But it's a really bad habit to store more than one piece of data into one single field. You should preferably split this data into a new table, using foreign keys. Otherwise, you will be stuck with similar workarounds, without any efficient way to use indexes, and therefore low performances.
Use this:-
SELECT * from productinfo WHERE products like "66523,%" OR products like ",66523,%" OR products like "%,66523"
It will match anything containing 66523, + something. OR , + 66523, + something. OR something + 66523(last number). It will make sure that whole number is matched.
SQL FIDDLE DEMO
Have you tried like this?
SELECT * from productinfo WHERE products like "66523,%" OR products like "%,66523,%" OR products like "%,66523" or products="66523";

How to select an item in a comma separated list field in MySQL

I have a table named product, where in the category field I have inserted around 5 to 6 categories.
For example 2,3,4,5,12 just like that.
But when I use this query it doesn't work:
SELECT * FROM product WHERE category in '3'
SELECT * FROM `product` WHERE `category` LIKE '%3%'
can anyone help to fix it
SELECT * FROM product WHERE FIND_IN_SET('3',category);
See: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
If you don't have find_in_set this code would be equivalent:
SELECT * FROM product
WHERE category = '3' or category LIKE '%,3,%'
or category LIKE '3,%' or category LIKE '%,3';
Otherwise you will get a false positive for 3 on '13, 23, 40'.
Warning
Do note however that it's very bad practise to put a CSV list in a field.
You'll get much better performance by extracting the list out of the field and putting it in another table in a 1-to-N relation.
the best practice would be to create another table!
i.e.:
product_categories
int productid
int category
SELECT * FROM product p
INNER JOIN product_categories pc ON pc.productid = p.productid
WHERE pc.category = '3'
with the way you want to implement it, you would also have a problem when a category '13' is available, but you want to have '3'
I'm sorry to say it but you should redesign your database.
Using a single field to store multiple connections will always be a pain in the butt.
It is much easier to create an additional table that stores the relations.
The IN statement wont work because you are asking for the category to be an exact match of "3" when it is in fact "2,3,4,5,12". I think it should also be in round brackets.
As user737767 said you should really normalise your database so you have a table of categories, a table of products and another table to show which categories each product belongs to.
Your LIKE statement is also a bad idea because searching for "3" will also bring back results like "23"