MySQL LIKE IN with Input Parameter of stored procedure Comma Separated Values
-
23-02-2021 - |
Question
I am creating a stored procedure to search user's Towns.
From the User Interface user will select multiple towns that will be sent to the stored procedure.
Sample input of the towns parameter for the stored procedure would be like orangi,faisal,nazimabad
.
Towns that are stored in the table are
- Orangi
- Korangi
- Shah Faisal
- Shahrah-e-Faisal
- Nazimabad
- North Nazimabad
Now I want to select all those above records against that input. That is I want to use LIKE
operator with IN
from a stored procedure's parameter.
I don't know how many towns would be there in the input. it could be just one, or two or three or all these mentioned above.
I want to search all the towns against that query
Sample Stored Procedure is
CREATE PROCEDURE `GetUsersByTown`(
IN `SearchTowns` VARCHAR(200) -- `ORANGI,FAISAL,NAZIMABAD`
)
BEGIN
SELECT
name,
Town,
FROM userLocation where Town in (SearchTowns)
END
Expected Outcome
- Name --- Town
- Hamza --- Orangi
- Jamal --- Korangi
- Hasham --- Shah Faisal
- Aliyan --- Shahrah-e-Faisal
- Danish --- Nazimabad
- Farrukh --- North Nazimabad
I have tried to use find_in_set()
but FIND_IN_SET does not accept wildcards like %
and it doesn't use index?
I have also looked at using REGEXP ValueA|ValueB
but I don't know how to put the input parameter that is comma separated values in that REGEXP
Solution
- Use
REPLACE(SearchTowns, ',', '|')
to change commas to pipes. - Put that result in, say,
@regexp
. Construct, via
CONCAT
a singleSELECT
withSET @sql = CONCAT('SELECT name, Town, FROM userLocation where Town REGEXP "', @regexp, '"');
PREPARE
andEXECUTE
the query.
That query should look like:
SELECT
name,
Town,
FROM userLocation where Town REGEXP "orangi|faisal|nazimabad"
(You could manually run that to verify that the correct rows are delivered.)
Also, be sure to have case-folding COLLATION
, such as utf8mb4_unicode_520_ci
for Town
. (That is indicated by the trailing _ci
.)
Code
DELIMITER //
CREATE PROCEDURE `GetUsersByTown`(
IN `SearchTowns` VARCHAR(200) -- `ORANGI,FAISAL,NAZIMABAD`
)
BEGIN
IF (SearchTowns IS NULL) THEN
SELECT name, Town
FROM userLocation;
ELSE
SET @towns = REPLACE(SearchTowns, ',', '|');
SELECT name, Town
FROM userLocation
WHERE Town REGEXP (@towns);
END IF;
END //
DELIMITER ;
OTHER TIPS
CREATE PROCEDURE GetUsersByTown
(
IN SearchTowns
LONGTEXT
)
BEGIN
SELECT
name,
Town,
FROM userLocation where FIND_IN_SET(Town,(SearchTowns))>0
END
This is a stored procedure which splits inputed comma-separated string (SearchTowns) into separate rows, inserts these rows into temporary table (Towns) and then uses join between userLocation and Towns tables on required conditions (where userLocation.town is like %town% from Towns table). All other rows which don't correspond this condition will be eliminated from the final resultset.
Also I used DISTINCT to avoid rows duplication in case if userLocation.town corresponds to more than one row from Towns table. For example, if SearchTowns contains 'Orangi' and 'Korangi' then users who are located in Korangi will be joined with both towns (because 'korangi' like '%orangi%').
CREATE PROCEDURE GetUsersByTown(SearchTowns VARCHAR(200))
BEGIN
DECLARE pos INT Default 0 ;
DECLARE str VARCHAR(200);
CREATE TEMPORARY TABLE Towns(town VARCHAR(200));
#Loop through comma-separated values
#pos - is the number of current word within search string
simple_loop: LOOP
SET pos=pos+1;
SET str=REPLACE(SUBSTRING(SUBSTRING_INDEX(SearchTowns, ',', pos),
LENGTH(SUBSTRING_INDEX(SearchTowns, ',', pos -1)) + 1),
',', '');
IF str='' THEN
LEAVE simple_loop;
END IF;
#Do Inserts into temp table here with str going into the row
insert into Towns values (str);
END LOOP simple_loop;
SELECT DISTINCT
u.name,
u.Town
FROM userLocation AS u
JOIN Towns AS t ON u.Town LIKE CONCAT('%', t.town, '%');
DROP TEMPORARY TABLE Towns;
END