I have a column holds a comma-separated values.
1,2,3
4,6,7
2,3,8
12234,5467,232445,232455,11223
With given criteria of array (e.g., 1,4,9),
How can I select rows whose value contains any of given?
I mean when I am given with 1,4,9, I need to select
1,2,3 -- has 1
4,6,7 -- has 4
UPDATE
I have a table who has a column of comma-separated values of other entity's primary keys.
I understand the reason why the original table designer did this. The other entity actually resides in other database which means not joinable. Or he or she just wanted to do like this.
The STUDENT table
id name classes
---------------------------
1 John 1,2,3
2 Jane 2,8,233423423
The Criteria
With given comma-separated class numbers, find students who is attending any of them.
given: 1 -> select John
given: 233423423 -> select Jane
given: 1,233423423 -> select Both
You can use dynamic template for regular expression. For example:
SET #Criteria='1,4,9';
SELECT `name`
FROM STUDENT
WHERE STUDENT.classes REGEXP concat('(^|,)(', REPLACE(#Criteria, ',', '|'), ')(,|$)');
If you have an input 1,4,9 and you have to find rows where any of 1, 4, or 9 occur in a comma-separated list?
SELECT ...
FROM MyTable
WHERE FIND_IN_SET(1, mycolumn)
OR FIND_IN_SET(4, mycolumn)
OR FIND_IN_SET(9, mycolumn)
See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_find-in-set for information about this function.
This should illustrate to you that storing comma-separated lists is not a good idea for a relational database, when you want to treat the elements of the list as discrete values.
See also my answer to Is storing a delimited list in a database column really that bad?
Related
I have a table that stores the action id list of items in comma separated form and now I want to get the description list from ids in comma separated form with MySQL query.
I have two tables:
table1: action_master table2: object_action
id action items actions_id_list
------------- ---------------------------
1 talk Human 1,2,4
2 walk Dog 3
3 bark Fish 4,5
4 eat
5 swim
Now I want to write a select query that can produce an output as follows:
items actions_desc_list
-----------------------------
Human talk,walk,eat
Dog bark
Fish eat,swim
Note: I don't have option to store action description list in object_action table directly. I have to use the ids.
General consideration of situation
You should not store comma separated lists as values in a column. This breaks normal forms in database. You should think about normalization process. The reason you have an issue right now is because of that comma separated list got designed this way in the first place. Notice, there is no proper way to create FOREIGN KEY constraint as well in here.
Solution
That said, since you are more likely than not, not able to perform schema redesign (if possible, at least inform someone who can to avoid future problems) you could use FIND_IN_SET MySQL function as a JOIN condition, and then GROUP_CONCAT your results.
FIND_IN_SET(f1, f2) returns the position of f1 string within f2 string. If we put it in a condition of > 0 it will return TRUE for when f1 exists within f2.
In your case:
f1 == action_master.id
f2 == object_action.actions_id_list
Sample data
create table action_master( id int, action varchar(10));
create table object_action (items varchar(10), actions_id_list varchar(15));
insert into action_master values (1,'talk'), (2,'walk'), (3,'bark'), (4,'eat'), (5,'swim');
insert into object_action values ('Human','1,2,4'),('Dog','3'),('Fish','4,5');
Query to produce result
select
oa.items
, group_concat(am.action order by am.id) as actions_desc_list
from object_action oa
inner join action_master am on
find_in_set(am.id, oa.actions_id_list) > 0
group by oa.items
See LIVE DEMO
Result
items actions_desc_list
-------------------------
Dog bark
Fish eat,swim
Human talk,walk,eat
Apologies for the title as I'm a bit unsure how to phrase this myself so hopefully an example might help.
I've got a MySQL table that holds questions
question_id, PK Int
text - Text
I also have a table called value that looks like this
value_id, PK Int
value - varchar
I think I might need a mapping table for this along the lines of
question_to_value
question_id int
value_id int
Though if my example looks like I don't need one then I can change the structure
Basically, given a single or multiple value_ids I want to pick the question that should be asked
so if I am given value_ids 1,2 I should have a unique question_id relating to those ids in the database. Given a value_id of 3 there should be a different question_id, and an input of value_ids (1,2,3) or 1,3 should again retrieve unique question_ids for both permutations.
I'm struggling with how I should go about it. Should I use a) a sort of joining table for this, and b) Most efficient way of querying it.
My initial thought it to have a question_to_value table that holds a question_id and value_id on a 1-1 basis, then doing the following
select question_id from question WHERE value_id in (?,?,?) but i'm not sure if this is the optimal way to structure this. Especially as the trouble is using the 'IN' query above if I'm just given the value_id of 1, it would actually bring back all the questions where value_id '1' is the only or part of the group of values to product a particular question. e.g
question_id 1 maps to value 1
question_id 2 maps to values of 1 and 2.
my in statement would bring back question_ids 1 and 2 for a value_id of 1 when I only want question_id 1 as it should match all criteria.
Any ideas on how I should structure this?
*** editing I'm trying to come up with another way of phrasing this to avoid confusion so hopefully the following will help
consider I have 4 'questions'
a
b
c
d
If I'm given an input of 1 I only want to retrieve a.
If I'm given 2 inputs of 1 and 2 I only want to retrieve b.
If I'm given 3 inputs of 1,2,3 I only want to retrieve c.
If I'm given 2 inputs of 1 and 3 I only want to retrieve d.
I'm trying to break the problem apart into component parts to define the question in a way that can be expressed in code.
I'm working on the idea that the proper question must match two rules:
The question must have an entry for each of the input values
The question must not have entries for any other input value
First, consider a query that returns all the value ids for the inputs.
SELECT value_id from values where `value` IN <inputs>
I don't know what language you are using, so I can't tell you how to build the inputs list. In php, it would be something like:
"('" . implode("','" $inputs) . "')"
to properly wrap each option in quotes (assuming the values don't also have quotes, but that's a separate, language-specific problem to solve).
Now it is simple to use that query to create a query that returns questions that have ANY of the input values:
SELECT question_id
FROM mapping_table
WHERE value_id IN (SELECT value_id from values where `value` IN <inputs>)
Finally, we can tweak that query to only return the question_ids with the right number of matches. We want one row for each question, and we only want questions that match all the values:
SELECT question_id
FROM mapping_table
WHERE value_id IN (SELECT value_id from values where `value` IN <inputs>)
GROUP BY question_id
HAVING COUNT(question_id) = <number of inputs>
That will give you a list of question_ids that match constraint 1 above. It will not test for constraint 2 in every case; if the input is (1,2) then this will match the question for (1,2) and the question for (1,2,3).
However, if you only get one row back from this query, there is no need to filter that list with the second query.
If you did get more than one match for the above, the second query is pretty simple - it's similar to the above query but with a different WHERE clause. This will get all the mapping table entries for the ids from the first query, and match the ones that don't have any extras:
SELECT question_id
FROM mapping_table
WHERE question_id IN <ids from first query>
GROUP BY question_id
HAVING COUNT(question_id) = <number of inputs>
You could combine the two queries, but unless performance is an issue, I'd keep them separate to make things easier to maintain.
NOTE: there is nothing here to constrain your data to make sure it is valid. In other words, there's nothing to prevent two questions from matching (1,2) when the questions are added to the database.
is there any way, i can get a field values (comma seperate) into an array or temporary table.
ex: i have field following with values 3,7,23,45
i want get them into an array without using PHP, or into a temporary table.
as i need to do some joint queries based on those values.
any help is appreciated
thanks
my table name is: shoes
field name is following
sample table values are like these
+----------+-----------+
userId following
+----------+-----------+
1 5,7,8,12
2 5,2,1,67
now, when i search for userId 1, i want to get values 5,7,8,12 into an array or temp table.
It is possible although not particularly quick. The best solution is to redesign the database to move the comma separated list into a different table, with one row for each comma separated element.
However if you want an SQL way to do it then something like the following will do it (this relies on having a table called integers with a single column called i, with 10 rows with the values 0 to 9).
SELECT DISTINCT shoes.userId , substring_index(substring_index(shoes.FollowingIdString, ',', anInt), ',', -1) AS SplitField
FROM shoes,
(SELECT a.i+b.i*10+c.i*100 AS anInt
FROM integers a, integers b, integers c) Sub1
HAVING SplitField IS NOT NULL
Assuming you have a table PersonList which has multiple columns and this is the table where you want to join to get the name of the followers list. eg
PersonTable
ID Name
1 AAA
2 BBB
there is a mysql function called FIND_IN_SET, eg
SELECT a.userID, GROUP_CONCAT(b.Name)
FROM Follower a
INNER JOIN Person b
ON FIND_IN_SET(b.id, a.following) > 0
GROUP BY a.userID
SQLFiddle Demo
I have inherited a database in which a person table has a field called authorised_areas. The front end allows the user to choose multiple entries from a pick list (populated with values from the description field of the area table) and then sets the value of authorised_areas to a comma-delimited list. I am migrating this to a MySQL database and while I'm at it, I would like to improve the database integrity by removing the authorised_areas field from the person table and create a many-to-many table person_area which would just hold pairs of person-area keys. There are several hundred person records, so I would like to find a way to do this efficiently using a few MySQL statements, rather than individual insert or update statements.
Just to clarify, the current structure is something like:
person
id name authorised_areas
1 Joe room12, room153, 2nd floor office
2 Anna room12, room17
area
id description
1 room12
2 room17
3 room153
4 2nd floor office
...but what I would like is:
person
id name
1 Joe
2 Anna
area
id description
1 room12
2 room17
3 room153
4 2nd floor office
person_area
person_id area_id
1 1
1 3
1 4
2 1
2 2
There is no reference to the area id in the person table (and some text values in the lists are not exactly the same as the description in the area table), so this would need to be done by text or pattern matching. Would I be better off just writing some php code to split the strings, find the matches and insert the appropriate values into the many-to-many table?
I'd be surprised if I were the first person to have to do this, but google search didn't turn up anything useful (perhaps I didn't use the appropriate search terms?) If anyone could offer some suggestions of a way to do this efficiently, I would very much appreciate it.
While it is possible to do this I would suggest that as a one off job it would probably be quicker to knock up a php (or your favorite scripting language) script to do it with multiple inserts.
If you must do it in a single statement then have a table of integers (0 to 9, cross join against itself to get as big a range as you need) and join this against your original table, using string functions to get the Xth comma and from that each of the values for each row.
Possible, and I have done it but mainly to show that having a delimited field is not a good idea. It would likely be FAR quicker to knock up a script with multiple inserts.
You could base an insert on something like this SELECT (although this also comes up with a blank line for each person as well as the relevant ones, and will only cope with up to 1000 authorised areas per person)
SELECT z.id, z.name, x.an_authorised_area
FROM person z
LEFT OUTER JOIN (
SELECT DISTINCT a.id, SUBSTRING_INDEX( SUBSTRING_INDEX( authorised_areas, ",", b.ournumbers ) , ",", -1 ) AS an_authorised_area
FROM person a, (
SELECT hundreds.i *100 + tens.i *10 + units.i AS ournumbers
FROM integers AS hundreds
CROSS JOIN integers AS tens
CROSS JOIN integers AS units
)b
)x ON z.id = x.id
I need help for this problem.
In MYSQL Table i have a field :
Field : artist_list
Values : 1,5,3,401
I need to find all records for artist uid 401
I do this
SELECT uid FROM tbl WHERE artist_list IN ('401');
I have all record where artist_list fields values are '401' only, but if i have 11,401 this query do not match.
Any idea ?
(I cant user LIKE method because if artist uid is 3 (match for 30, 33, 3333)...
Short Term Solution
Use the FIND_IN_SET function:
SELECT uid
FROM tbl
WHERE FIND_IN_SET('401', artist_list) > 0
Long Term Solution
Normalize your data - this appears to be a many-to-many relationship already involving two tables. The comma separated list needs to be turned into a table of it's own:
ARTIST_LIST
artist_id (primary key, foreign key to ARTIST)
uid (primary key, foreign key to TBL)
Your database organization is a problem; you need to normalize it. Rather than having one row with a comma-separated list of values, you should do one value per row:
uid artist
1 401
1 11
1 5
2 5
2 4
2 2
Then you can query:
SELECT uid
FROM table
WHERE artist = 401
You should also look into database normalization because what you have is just going to cause more and more problems in the future.
SELECT uid
FROM tbl
WHERE CONCAT(',', artist_list, ',') LIKE '%,401,%'
Although it would make more sense to normalise your data properly in the first place. Then your query would become trivial and have much better performance.