how to pass multiple variables in WHERE ... IN in stored procedure? [duplicate] - mysql

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,%';

Related

select data mysql

i have in my table places named field. there are space separated values(there are problem to store csv value in one field). now i want to fire query like below. how i can do ??
select * from tablename where variablename in places
i did try this way but it shows syntax error.
select * from tablename where variablename in replace(places,' ',',')
### places ###
bank finance point_of_interest establishment
Use FIND_IN_SET
For comma separated
SELECT *
FROM tablename
WHERE ( FIND_IN_SET( 'bank', variablename ) )
Refer : SQL Fiddle
For space separated
SELECT *
FROM tablename
WHERE ( FIND_IN_SET( 'bank', replace(variablename,' ',',') ) )
Refer : SQL Fiddle
The best solution would be to normalise your data structure and do not have a single field storing multiple values.
You can make a query work without normalisation, but any solutions would be lot less optimal from a performance point of view.
Use patter matching with like operator:
... where fieldname like '% searched_value %'
Use the replace() function and combine it with find_in_set():
... where find_in_set('searched_value',replace(fieldname,' ',','))>0
Hi I think your problem comes from the usage of IN
IN for MySql is used like this
SELECT *
FROM table_name
WHERE column_name IN (bank,finance,point_of_interest, establishment);
In case of you want to select places you need to specify each place into value like

Query MySQL field for LIKE string

I have a field called 'areasCovered' in a MySQL database, which contains a string list of postcodes.
There are 2 rows that have similar data e.g:
Row 1: 'B*,PO*,WA*'
Row 2: 'BB*, SO*, DE*'
Note - The strings are not in any particular order and could change depending on the user
Now, if I was to use a query like:
SELECT * FROM technicians WHERE areasCovered LIKE '%B*%'
I'd like it to return JUST Row 1. However, it's returning Row 2 aswell, because of the BB* in the string.
How could I prevent it from doing this?
The key to using like in this case is to include delimiters, so you can look for delimited values:
SELECT *
FROM technicians
WHERE concat(', ', areasCovered, ', ') LIKE '%, B*, %'
In MySQL, you can also use find_in_set(), but the space can cause you problems so you need to get rid of it:
SELECT *
FROM technicians
WHERE find_in_set('B', replace(areasCovered, ', ', ',') > 0
Finally, though, you should not be storing these types of lists as strings. You should be storing them in a separate table, a junction table, with one row per technician and per area covered. That makes these types of queries easier to express and they have better performance.
You are searching wild cards at the start as well as end.
You need only at end.
SELECT * FROM technicians WHERE areasCovered LIKE 'B*%'
Reference:
Normally I hate REGEXP. But ho hum:
SELECT * FROM technicians
WHERE concat(",",replace(areasCovered,", ",",")) regexp ',B{1}\\*';
To explain a bit:
Get rid of the pesky space:
select replace("B*,PO*,WA*",", ",",");
Bolt a comma on the front
select concat(",",replace("B*,PO*,WA*",", ",","));
Use a REGEX to match "comma B once followed by an asterix":
select concat(",",replace("B*,PO*,WA*",", ",",")) regexp ',B{1}\\*';
I could not check it on my machine, but it's should work:
SELECT * FROM technicians WHERE areasCovered <> replace(areaCovered,',B*','whatever')
In case the 'B*' does not exist, the areasCovered will be equal to replace(areaCovered,',B*','whatever'), and it will reject that row.
In case the 'B*' exists, the areCovered will NOT be eqaul to replace(areaCovered,',B*','whatever'), and it will accept that row.
You can Do it the way Programming Student suggested
SELECT * FROM technicians WHERE areasCovered LIKE 'B*%'
Or you can also use limit on query
SELECT * FROM technicians WHERE areasCovered LIKE '%B*%' LIMIT 1
%B*% contains % on each side which makes it to return all the rows where value contains B* at any position of the text however your requirement is to find all the rows which contains values starting with B* so following query should do the work.
SELECT * FROM technicians WHERE areasCovered LIKE 'B*%'

Match comma seperated list with input in SQL statement

Im optimizing my SQL statement to make it faster.
I have a comma seperated list with zipcodes like
1111, 1112,1115,1112 etc etc
Now in my query I want to match if the iput matches 1 of those zipcodes. If so.. then it will return a ID of the object that has all those zipcodes.
But what is the best way to do this now im doing
AND ( loc.loc_zip LIKE '%".$_REQUEST['zip']."%'
Validation of the input will be added ofcourse.. but this is just for testing. But I have tested this and it seems a bit slow.
Is this the best way to do this ?
you should use 'in'
select * from Users where userid in (1,2,3,4,45,6,656)
Edit:
if the ZipCodes are Chars, you can only use in if you separate them by ''
select * from loc where loc.loc_zip in ('1111','1112','1115','1112')
if .$_REQUEST['zip']. has ' ' then->
select * from loc where loc.loc_zip in (.$_REQUEST['zip'].)
if the ZipCodes are int you can use the first statement
Bare in mid you must intersect your list with '' or it wont work

MYSQL explode search

I have a field that is build like this "1;2;3;4;8;9;11;"
If I want to search if a number is in this range I do it like this:
SELECT * FROM table WHERE [id] LIKE '[number];%' OR '%;[number];%'
Is there another more easy way where i can split the string?
Many thanks
If you are storing the values in a string, the best way to use like is as:
SELECT *
FROM table
WHERE concat(';', #numbers) like concat('%;', [id], ';%')
MySQL also offers find_in_set() when the delimiter is a comma:
SELECT *
FROM table
WHERE find_in_set(id, replace(#numers, ';', ',')
Use IN() with a comma delimited string of IDs
SELECT * FROM table WHERE id IN(1,2,3,4,8,9,11)
create another table called helper with 2 fields: number and id. you will need to programmatically create it by splitting the id field and add one row to this table for each number in each id.
create an index this table on number
select table.* from table where id in (select id from helper where number = 23)
you can use this
SELECT name ,`from`, find_in_set('4' , REPLACE(`from`, ';', ',')) as the_place_of_myval FROM reservation
having the_place_of_myval != 0
note: that in my query im searching for strings which have number 4
demo here in my demo there is two examples.

MySQL QUERY condition

I try make search for my site. Example: have field name = city, if city is empty my query looks: SELECT * FROM TABLE WHERE CITY = ALL;
I don't know how to write: CITY = ALL correct, expression CITY is not null will be complicated because I should be remove =
Try this:
SELECT *
FROM Table
WHERE #SearchParam IS NULL OR City = #SearchParam
If his parameter #SearchParam is passed to the query with a NULL value then it will return all the data in the table, otherwise it will search for the cities with this parameter.
remove the where completely
SELECT * FROM TABLE
or
SELECT * FROM TABLE WHERE CITY = #searchstring or #searchstring is null;
If you don't care about very optimized queries, you can use a like operator compare with
City like '%#exp'
If exp is empty the query returns all Cities.
I personally do not recommend this :)
All the best
If you are getting them all, why have the WHERE clause in at all?
SELECT * FROM TABLE
Note, you should avoid using SELECT * whenever possible. As it is a waste of resources, always be explicit with your columns returned.
Maybe something like this?
SELECT * FROM TABLE WHERE (CITY = SOMETHING) OR (SOMETHING is null);