Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MySQL String separation by comma operator

I have String asdasdwdfef,rgrgtggt,weef and i want output like in table format as shown below

id      decription
1       asdasdwdfef
2       rgrgtggt
3       weef

For this i created a procedure here is my procedure

DELIMITER ;;
CREATE Procedure Split(_RowData text, _Delimeter text)
BEGIN
    DECLARE _Iterator INT default 1;
    DECLARE _FoundIndex INT;
    DECLARE _Data varchar(255);
    SET _FoundIndex = LOCATE(_Delimeter,_RowData);
    DROP TABLE IF EXISTS _RtnValue;
    CREATE temporary TABLE _RtnValue(ID INT AUTO_INCREMENT NOT NULL, description text, primary key(ID));
    WHILE _FoundIndex > 1 DO
        INSERT INTO _RtnValue (description)
        SELECT
        _Data = LTRIM(RTRIM(SUBSTRING(_RowData, 1, _FoundIndex - 1)));
        set _RowData = SUBSTRING(_RowData, _FoundIndex + LENGTH(_Delimeter) / 2, LENGTH(_RowData));
        SET _Iterator = _Iterator + 1;
        SET _FoundIndex = LOCATE(_Delimeter, _RowData);
    END WHILE;
    INSERT INTO _RtnValue(description) SELECT _Data = LTRIM(RTRIM(_RowData));
    select * from _RtnValue;
END

But when i execute it by using following command

call Split('asdasdwdfef,rgrgtggt,weef', ',');

it gives me the following output:

id      decription
1       NULL
2       NULL
3       NULL

Please let me know how to fix this issue. I am using MySQL.

like image 888
user3441151 Avatar asked Oct 19 '22 10:10

user3441151


2 Answers

I got the answer

First create new function

CREATE FUNCTION SPLIT_STR(x VARCHAR(255), delim VARCHAR(12), pos INT)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1), delim, '');

Then create stored procedure

DELIMITER ;;
CREATE PROCEDURE Split(in fullstr varchar(255))
BEGIN
    DECLARE a INT Default 0 ;
    DECLARE str VARCHAR(255);

    DROP TABLE IF EXISTS my_temp_table;
    CREATE temporary TABLE my_temp_table(ID INT AUTO_INCREMENT NOT NULL, description text, primary key(ID));

    simple_loop: LOOP
        SET a=a+1;
        SET str=SPLIT_STR(fullstr,",",a);
        IF str='' THEN
            LEAVE simple_loop;
        END IF;
        #Do Inserts into temp table here with str going into the row
        insert into my_temp_table (description) values (str);
   END LOOP simple_loop;
   select * from my_temp_table;
END

After that when i call it by call Split('asas,d,sddf,dfd'); it gives me the output that what i want.

Thanx for every suggestion.

like image 136
user3441151 Avatar answered Oct 21 '22 04:10

user3441151


Requirements: split a 'delimited' column into rows (a table).

Why? You can use all the SQL Aggregate functions to process the rows.

Working SQLFiddle

Related questions that use this code:

  • Count number of unique characters in a string

  • Can I resolve this with pure mysql? (joining on ';' separated values in a column)

I chose to use:

  • table of integers (integersequence) which when 'inner joined' or 'cross joined' (same thing) to another table will generate rows which are certain to have a unique sequence number starting from 1. This is just using what RDBMS engines are required to do.

  • why a table of integers?: It is small and will get loaded into the cache. It is easy to understand.

  • Rather than have lots of code in the query that obscures what it actually does. I use functions that do one job. It simplifies the main query and can be tested and checked separately. I am only concerned about ease of maintenance and understanding at this point.

The example here is for one string. However, It can be easily expanded to a table of delimited strings of various sizes. The 'cross join' will create all the possible options of index starting from 1 when joining to integersequence (i really need to use another name :-/).

So, the query:

SET @StrToParse = "asdasdwdfef,rgrgtggt,weef";

/* example */
select integerseries.id,
       count_in_set(@StrToParse, ','),
       value_in_set(@StrToParse, ',', integerseries.id)
from integerseries
where integerseries.id <= count_in_set(@StrToParse, ',');

The output:

PositionOfString, CountOfCommaDelimitedStrings, StringAtThatPosition 

1                 3                             asdasdwdfef
2                 3                             rgrgtggt
3                 3                             weef

Now, how do we get those values:

I chose to use two functions:

The names are related to the mysql function 'FIND_IN_SET'.

1) The COUNT_IN_SET function: returns the count of character delimited items in the column.

CREATE FUNCTION `COUNT_IN_SET`(haystack VARCHAR(1024), 
                               delim CHAR(1)
                               ) RETURNS INTEGER
BEGIN
      RETURN CHAR_LENGTH(haystack) - CHAR_LENGTH( REPLACE(haystack, delim, '')) + 1;
END$$

2) The VALUE_IN_SET function: treats the delimited list as a one based array and returns the value at the given 'index'.

CREATE FUNCTION `VALUE_IN_SET`(haystack VARCHAR(1024), 
                               delim CHAR(1), 
                               which INTEGER
                               ) RETURNS VARCHAR(255) CHARSET utf8 COLLATE utf8_unicode_ci
BEGIN
      RETURN  SUBSTRING_INDEX(SUBSTRING_INDEX(haystack, delim, which),
                     delim,
                     -1);
END$$

The intereseries table - see (Tally tables)

CREATE TABLE `integerseries` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

/*Data for the table `integerseries` */

insert  into `integerseries`(`id`) values (1);
insert  into `integerseries`(`id`) values (2);
insert  into `integerseries`(`id`) values (3);
insert  into `integerseries`(`id`) values (4);
insert  into `integerseries`(`id`) values (5);
insert  into `integerseries`(`id`) values (6);
insert  into `integerseries`(`id`) values (7);
insert  into `integerseries`(`id`) values (8);
insert  into `integerseries`(`id`) values (9);
insert  into `integerseries`(`id`) values (10);
like image 44
Ryan Vincent Avatar answered Oct 21 '22 06:10

Ryan Vincent