I need to search value starting with given string in comma separated values.
Example,
I have '1_2_3_4_5_6, 1_2_3_4_5_8, 1_2_3_6_5_8' in my column. I can search for rows with exact value using
select * from table where find_in_set('1_2_3_4_5_6',column)
But how to search, if starting part of the string is given? something like this:
select * from table where find_in_set('1_2_3%',column) ?
If I understand you correctly (I'm still not sure I do), you could just use:
SELECT * FROM table WHERE column LIKE '%1_2_3%';
This would give you columns where the value is like:
1_2_3_4_5_5
1_4_5_, 1_2_3_4_5_, 6_7
and so on.
But you should really normalize your tables. This is important for good queries and performance wise also important.
According to #Xatenev suggestions, if you really like only the values and the row of each matching row, this won't work so well and will be a lot of overhead. This are the steps that I would perform:
Split all CSV columns into multiple rows (this is a hack and a performance killer, I found some working solution but did not test it, see here): Pseudo Code: SELECT ID, SPLIT(',', column) AS entries FROM table (NOT WORKING)
Filter the new virtual table to select only rows that match the prefix (SELECT * FROM virtual_table WHERE find_in_set("1_2_3%, entries) ORDER BY ID)
Concatenate the matching entries back into a list for each ID. e.g. SELECT ID, GROUP_CONCAT(entries SEPARATOR ', ') FROM filtered_table GROUP BY ID
Do something
The unknown part is the beginning with the split in multiple rows. There are a lot of possible solutions all with their own drawbacks or advantages. Be aware that this will always (regardless of the selected method) will cost a lot of performance.
ADDITIONAL NODE:
It could be adventures in your situation, that you get each row matching your search string like in my first example and filter them in memory. This might be faster than doing this in MYSQL.
you can try with 'REGEXP'
If you want to match data with subtring, please try this
SELECT * FROM `table` WHERE `column` REGEXP '[, ]?1_2_3[^,]*,?' ;
Or
If you want to exact start match, please try this
SELECT * FROM `table` WHERE `column` REGEXP '[, ]?1_2_3[^,]*,?' AND `column` NOT REGEXP '[^, ]1_2_3[^,]*,?' ;
I was able to solve it with no-regex. Sonam's Answer is correct as well.
SELECT * from table WHERE CONCAT(',', columnname, ',') like '%,1_2_3%'
I have a column in one of my table where I store multiple ids seperated by comma's.
Is there a way in which I can use this column's value in the "IN" clause of a query.
The column(city) has values like 6,7,8,16,21,2
I need to use as
select * from table where e_ID in (Select city from locations where e_Id=?)
I am satisfied with Crozin's answer, but I am open to suggestions, views and options.
Feel free to share your views.
Building on the FIND_IN_SET() example from #Jeremy Smith, you can do it with a join so you don't have to run a subquery.
SELECT * FROM table t
JOIN locations l ON FIND_IN_SET(t.e_ID, l.city) > 0
WHERE l.e_ID = ?
This is known to perform very poorly, since it has to do table-scans, evaluating the FIND_IN_SET() function for every combination of rows in table and locations. It cannot make use of an index, and there's no way to improve it.
I know you said you are trying to make the best of a bad database design, but you must understand just how drastically bad this is.
Explanation: Suppose I were to ask you to look up everyone in a telephone book whose first, middle, or last initial is "J." There's no way the sorted order of the book helps in this case, since you have to scan every single page anyway.
The LIKE solution given by #fthiella has a similar problem with regards to performance. It cannot be indexed.
Also see my answer to Is storing a delimited list in a database column really that bad? for other pitfalls of this way of storing denormalized data.
If you can create a supplementary table to store an index, you can map the locations to each entry in the city list:
CREATE TABLE location2city (
location INT,
city INT,
PRIMARY KEY (location, city)
);
Assuming you have a lookup table for all possible cities (not just those mentioned in the table) you can bear the inefficiency one time to produce the mapping:
INSERT INTO location2city (location, city)
SELECT l.e_ID, c.e_ID FROM cities c JOIN locations l
ON FIND_IN_SET(c.e_ID, l.city) > 0;
Now you can run a much more efficient query to find entries in your table:
SELECT * FROM location2city l
JOIN table t ON t.e_ID = l.city
WHERE l.e_ID = ?;
This can make use of an index. Now you just need to take care that any INSERT/UPDATE/DELETE of rows in locations also inserts the corresponding mapping rows in location2city.
From MySQL's point of view you're not storing multiple ids separated by comma - you're storing a text value, which has the exact same meaing as "Hello World" or "I like cakes!" - i.e. it doesn't have any meaing.
What you have to do is to create a separated table that will link two objects from the database together. Read more about many-to-many or one-to-many (depending on your requirements) relationships in SQL-based databases.
Rather than use IN on your query, use FIND_IN_SET (docs):
SELECT * FROM table
WHERE 0 < FIND_IN_SET(e_ID, (
SELECT city FROM locations WHERE e_ID=?))
The usual caveats about first form normalization apply (the database shouldn't store multiple values in a single column), but if you're stuck with it, then the above statement should help.
This does not use IN clause, but it should do what you need:
Select *
from table
where
CONCAT(',', (Select city from locations where e_Id=?), ',')
LIKE
CONCAT('%,', e_ID, ',%')
but you have to make sure that e_ID does not contain any commas or any jolly character.
e.g.
CONCAT(',', '6,7,8,16,21,2', ',') returns ',6,7,8,16,21,2,'
e_ID=1 --> ',6,7,8,16,21,2,' LIKE '%,1,%' ? FALSE
e_ID=6 --> ',6,7,8,16,21,2,' LIKE '%,6,%' ? TRUE
e_ID=21 --> ',6,7,8,16,21,2,' LIKE '%,21,%' ? TRUE
e_ID=2 --> ',6,7,8,16,21,2,' LIKE '%,2,%' ? TRUE
e_ID=3 --> ',6,7,8,16,21,2,' LIKE '%,3,%' ? FALSE
etc.
Don't know if this is what you want to accomplish. With MySQL there is feature to concatenate values from a group GROUP_CONCAT
You can try something like this:
select * from table where e_ID in (Select GROUP_CONCAT(city SEPARATOR ',') from locations where e_Id=?)
this one in for oracle ..here string concatenation is done by wm_concat
select * from table where e_ID in (Select wm_concat(city) from locations where e_Id=?)
yes i agree with raheel shan .. in order put this "in" clause we need to make that column into row below code one do that job.
select * from table where to_char(e_ID)
in (
select substr(city,instr(city,',',1,rownum)+1,instr(city,',',1,rownum+1)-instr(city,',',1,rownum)-1) from
(
select ','||WM_CONCAT(city)||',' city,length(WM_CONCAT(city))-length(replace(WM_CONCAT(city),','))+1 CNT from locations where e_Id=? ) TST
,ALL_OBJECTS OBJ where TST.CNT>=rownum
) ;
you should use
FIND_IN_SET Returns position of value in string of comma-separated values
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2
You need to "SPLIT" the city column values. It will be like:
SELECT *
FROM table
WHERE e_ID IN (SELECT TO_NUMBER(
SPLIT_STR(city /*string*/
, ',' /*delimiter*/
, 1 /*start_position*/
)
)
FROM locations);
You can read more about the MySQL split_str function here: http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/
Also, I have used the TO_NUMBER function of Oracle here. Please replace it with a proper MySQL function.
IN takes rows so taking comma seperated column for search will not do what you want but if you provide data like this ('1','2','3') this will work but you can not save data like this in your field whatever you insert in the column it will take the whole thing as a string.
You can create a prepared statement dynamically like this
set #sql = concat('select * from city where city_id in (',
(select cities from location where location_id = 3),
')');
prepare in_stmt from #sql;
execute in_stmt;
deallocate prepare in_stmt;
Ref: Use a comma-separated string in an IN () in MySQL
Recently I faced the same problem and this is how I resolved it.
It worked for me, hope this is what you were looking for.
select * from table_name t where (select (CONCAT(',',(Select city from locations l where l.e_Id=?),',')) as city_string) LIKE CONCAT('%,',t.e_ID,',%');
Example: It will look like this
select * from table_name t where ',6,7,8,16,21,2,' LIKE '%,2,%';
I am currently working on a school system where we have a parent course and a child course (meta_courses in Moodle).
So, we have a table mdl_course_meta and it has 3 fields. Id, parent_course and child_course.
My problem is that a parent course can have many child courses so that means, for example, a parent_course = 50 can appear two times in the table which means it has 2 child courses. I just want to be able to find all the parent courses without it returning the same value twice or more times. I'm currently using this query right now which obviously doesn't do what I want:
$q = "SELECT * FROM mdl_course_meta";
I am working with PHP as well by the way.
Thanks a lot.
SELECT DISTINCT parent_course from mdl_course_meta
That should do it if you just want the course names. One thing to keep in mind, if you want other fields this is not going to work the way you want it to(how would it know which record to choose if there are multiple records with the same parent_course and you only want one).
This approach can only be used if you only want to return the parent_courses without duplicates.
DISTINCT helps to eliminate duplicates. If a query returns a result that contains duplicate rows, you can remove duplicates to produce a result set in which every row is unique. To do this, include the keyword DISTINCT after SELECT and before the output column list.
$q = "SELECT DISTINCT parent_course FROM mdl_course_meta";
If you don't want duplicate values in a single column, use GROUP BY parent_course.
In this way you are free to select any column.
If you only want distinct values for a particular column column, then you can use GROUP BY:
SELECT *
FROM mdl_course_meta
GROUP BY parent_course
The values in the other columns will be arbitrary. This will work in MySQL 5.x.
MySQL 4.x won't let you be arbitrary, so you can't mix aggregate and non-aggregate columns. Instead, you'd have to do something like this, which gets a bit complicated:
SELECT MAX(col1), MAX(col2), parent_course, MAX(col4), ...
FROM mdl_course_meta
GROUP BY parent_course
This way, the values aren't arbitrary. You've specified the ones you want.
I have a programme listing database with all the information needed for one programme packed into one table (I should have split programmes and episodes into their own) Now since there are multiple episodes for any given show I wish to display the main page with just the title names in ascending and chosen letter. Now I know how to do the basic query but this is all i know
SELECT DISTINCT title FROM programme_table WHERE title LIKE '$letter%'
I know that works i use it. But I am using a dynamic image loading that requires a series number to return that image full so how do I get the title to be distinct but also load the series number from that title?
I hope I have been clear.
Thanks for any help
Paul
You can substitute the DISTINCT keyword for a GROUP BY clause.
SELECT
title
, series_number
FROM
programme_table
WHERE title LIKE '$letter%'
GROUP BY
title
, series_number
There are currently two other valid options:
The option suggested by Mohammad is to use a HAVING clause in stead of the WHERE clause this is actually less optimal:
The WHERE clause is used to restrict records, and is also used by the query optimizer to determine which indexes and tables to use. HAVING is a "filter" on the final result set, and is applied after ORDER BY and GROUP BY, so MySQL cannot use it to optimize the query.
So HAVING is a lot less optimal and you should only use it when you cannot use 'WHERE' to get your results.
quosoo points out that the DISTINCT keyword is valid for all listed columns in the query. This is true, but generally people do not recommend it (there is no performance difference *In some specific cases there is a performance difference***)**. The MySQL optimizer however spits out the same query for both so there is no actual performance difference.
Update
Although MySQL does apply the same optimization to both queries, there is actually a difference: when DISTINCT is used in combination with a LIMIT clause, MySQL stops as soon as it finds enough unique rows. so
SELECT DISTINCT
title
, series_number
FROM
programme_table
WHERE
title LIKE '$letter%'
is actually the best option.
select title,series_number from programme_table group by title,series_number having title like '$letter%';
DISTINCT keyword works actually for a list of colums so if you just add the series to your query it should return a set of unique title, series combinations:
SELECT DISTINCT title, series FROM programme_table WHERE title LIKE '$letter%'
Hey thanks for that but i have about 1000 entries with the same series so it would single out the series as well rendering about 999 programmes useless and donot show.
I however found out away to make it unique and show the series number
SELECT * FROM four a INNER JOIN (SELECT title, MIN(series) AS MinPid FROM four WHERE title LIKE '$letter%' GROUP BY title) b ON a.title = b.title AND a.series = b.MinPid
Hopefully it helps anyone in the future and thank you for the replies :)