views:

583

answers:

2

When sending a string of comma separated ids, as a varchar, to a MySQL stored procedure I can not use the string as part of an IN clause with the correct results returned. The string is truncated as a decimal and only the first value is being used.

I thought I would be able to get around this by preparing and then executing the statement, but this still only returned a match for the first value.

Code examples may make things a bit clearer. I want to convert the following into a stored procedure (with the in clause dynamic):

select id, name from cities where id in (1,2,3);

This is my stored procedure using a prepared statement:

DROP PROCEDURE IF EXISTS `cities_select_by_ids` $$
CREATE PROCEDURE `cities_select_by_ids`(
    in _cityIds varchar(1000)
)
BEGIN
SET  @cityIds = _cityIds;

PREPARE stmt FROM '
    select
      id,
      name
    from cities
    where id in (?);
';

EXECUTE stmt USING @cityIds;
DEALLOCATE PREPARE stmt;

END $$
DELIMITER ;

Calling the stored procedure I only get a match for the city '1':

call cities_select_by_ids_prepare('1, 2, 3');

Here is a create and insert script for the table and data:

CREATE TABLE cities (
  id int(10) unsigned NOT NULL auto_increment,
  name varchar(100) NOT NULL,
  PRIMARY KEY  (`id`)
);
insert into cities (name) values ('London'), ('Manchester'), ('Bristol'), ('Birmingham'), ('Brighton');
A: 

due to the way parameterization works, this is not possible.

the closest you can get it this:

where find_in_set(id, ?)

but this will not scale as as can not use an index.

longneck
Thanks, this is what I worried - at least I now know though.
Nick
A: 

Try this.

DROP PROCEDURE IF EXISTS `cities_select_by_ids_2`;

CREATE PROCEDURE `cities_select_by_ids_2`(
    in cityIDs varchar(1000)
)

BEGIN

#- ix - index into the list of city IDs
# cid - city ID
SET @ix := 1;
SET @cid := substring_index(cityIDs, ',', @ix);

LOOP_1: 
WHILE (@cid is not null) DO
    SELECT id,  name 
    FROM cities
        WHERE id in (@cid) ;

     #-- substring_index returns complete cityIDs string when index is > number of elements
     IF (length(substring_index(cityIDs, ',', @ix)) >= length(cityIDs)) THEN
          LEAVE LOOP_1;
     END IF;

     SET @ix := @ix + 1;
     SET @cid = substring_index(substring_index(cityIDs, ',', @ix), ',', -1);

END WHILE;
END 

#----
call cities_select_by_ids_2('1, 2');
Shiva