I want to extract the substrings from a string in MySQL. The string contains multiple substrings separated by commas(','). I need to extract these substrings using any MySQL functions.
For example:
Table Name: Product
-----------------------------------
item_code name colors
-----------------------------------
102 ball red,yellow,green
104 balloon yellow,orange,red
I want to select the colors field and extract the substrings as red, yellow and green as separated by comma.
A possible duplicate of this: Split value from one field to two
Unfortunately, MySQL does not feature a split string function.
As in the link above indicates there are User-defined Split function.
A more verbose version to fetch the data can be the following:
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(colors, ',', 1), ',', -1) as colorfirst,
SUBSTRING_INDEX(SUBSTRING_INDEX(colors, ',', 2), ',', -1) as colorsecond
....
SUBSTRING_INDEX(SUBSTRING_INDEX(colors, ',', n), ',', -1) as colornth
FROM product;
Based on https://blog.fedecarg.com/2009/02/22/mysql-split-string-function/, here is a way to access a value from a delimiter separated array:
/*
usage:
SELECT get_from_delimiter_split_string('1,5,3,7,4', ',', 1); -- returns '5'
SELECT get_from_delimiter_split_string('1,5,3,7,4', ',', 10); -- returns ''
*/
CREATE FUNCTION get_from_delimiter_split_string(
in_array varchar(255),
in_delimiter char(1),
in_index int
)
RETURNS varchar(255) CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci
RETURN REPLACE( -- remove the delimiters after doing the following:
SUBSTRING( -- pick the string
SUBSTRING_INDEX(in_array, in_delimiter, in_index + 1), -- from the string up to index+1 counts of the delimiter
LENGTH(
SUBSTRING_INDEX(in_array, in_delimiter, in_index) -- keeping only everything after index counts of the delimiter
) + 1
),
in_delimiter,
''
);
here are the docs for the string operators for reference: https://dev.mysql.com/doc/refman/8.0/en/string-functions.html
Check the use of SPLIT_STR function here at line 64 to 69
Related
lets say i have a "varchar" variable which contains "ascii" code separating each other by a ',' and i want to convert it to character and insert it into a column. Is there any way that i can do this? I am new in mysql so i was wondering if someone can help.
Example: lets say we are inside a trigger or procedure.
declare test varchar(10);
set test = "73,116";
now i want to convert it into "it" and store it in a column of a table which is varchar as well.
Help me with this please.
Iteratively parsing a string is not an easy task for a set-based language such as SQL.
One option uses a recursive query (available in MySQL 8.0):
set #test = '73,116';
with recursive cte as (
select
1 n,
0 + substring(#test, 1, locate(',', #test) - 1) part,
concat(substring(#test, locate(',', #test) + 1), ',') rest
union all
select
n + 1,
substring(rest, 1, locate(',', rest) - 1),
substring(rest, locate(',', rest) + 1)
from cte
where locate(',', rest) > 0
)
select group_concat(char(part using utf8) order by n separator '') res
from cte;
The recursive query extracts each csv part sequentially, while keeping track of the position. Then, the outer query converts each ASCII code the corresponding character, and re-aggregates the results into a string.
Demo on DB Fiddle:
| res |
| --- |
| It |
I'm looking for a single query that's purely MySQL. The goal of this query is to utilize things such as SUBSTRING_INDEX, CONCAT, or whatever it needs to, in order to find a value in a string.
Let's say that the string looks something like this:
{"name":34,"otherName":55,"moreNames":12,"target":26,"hello":56,"hi":26,"asd":552,"p":3722,"bestName":11,"cc":6,"dd":10,}
My goal is to get the value of target, in this case, 26. However, "target":26 might not always be in that location in the string. Neither would any of the other properties. On top of that, the value might not always be 26. I need some way to check what number comes after "target": but before the , after "target":. Is there any way of doing this?
This one ?
create table sandbox (id integer, jsoncolumn varchar(255));
insert into sandbox values (1,'{"name":34,"otherName":55,"moreNames":12,"target":26,"hello":56,"hi":26,"asd":552,"p":3722,"bestName":11,"cc":6,"dd":10}');
mysql root#localhost:sandbox> SELECT jsoncolumn->'$.target' from sandbox;
+--------------------------+
| jsoncolumn->'$.target' |
|--------------------------|
| 26 |
+--------------------------+
https://dev.mysql.com/doc/refman/5.7/en/json-search-functions.html
Please try this function to get value from JSON string in MYSQL
DROP FUNCTION IF EXISTS CAP_FIRST_CHAR;
DELIMITER $$
CREATE FUNCTION getValueFromJsonSring(jsonStr VARCHAR(250), getKey VARCHAR(250))
RETURNS VARCHAR(250) deterministic
BEGIN
DECLARE output VARCHAR(250); -- Holds the final value.
DECLARE data VARCHAR(250); -- Holds the exctracted value from JSON
SET getKey=CONCAT('"',getKey,'"');
SET data= TRIM(LEADING ':' FROM
substring_index(
substring_index(
substring_index(
substring_index(
SUBSTRING(jsonStr, 2, LENGTH(jsonStr)-2)
, getKey , '2'),
getKey,
-1
)
, ',', '1'),
',',
-1
)
);
SET output =SUBSTRING(data, 2, LENGTH(data)-2);
RETURN output;
END;
$$
DELIMITER ;
SELECT getValueFromJsonSring('{"amount":"400.34","departmentId":"7","date":"2017-06-02","PONumber":"0000064873","vendor":"44"}',"departmentId");
I have been attempting to edit/add a value string column (process) within a table.
The current values in the string is as follow:
1:38,25:39,41:101
What I want to do is add 1000 to every value after "X:" so after the query the values should read:
1:1038,25:1039,41:1101
I have looked at CONCAT but that seems to only insert a value into a string within certain parameters. Any ideas?
You can use CAST CONCAT and SUBSTRING_INDEX functions to get the required output, e.g.:
SELECT
CONCAT(SUBSTRING_INDEX(value, ':', 1), ":", (CAST(SUBSTRING_INDEX(value, ':', -1) AS UNSIGNED) + 1000))
FROM test;
Here's the SQL Fiddle.
Use of variables can help you achieve what you want:
select #pre := SUBSTRING_INDEX(SUBSTRING_INDEX(`column_name`, ':', 1), '0', -1),
#post := SUBSTRING_INDEX(SUBSTRING_INDEX(`column_name`, ':', 2), '0', -1),
concat(#pre,":",#post+1000) as required_value from table_name;
References:
for use of variables:
https://dev.mysql.com/doc/refman/5.7/en/user-variables.html
for substring_index:
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring-index
You should normalize the data and store it in a separate table. That said, to answer your question you could use these queries to achieve what you want:
CREATE TEMPORARY TABLE temp (val VARCHAR(50));
SET #str = CONCAT("INSERT INTO temp (val) VALUES ('",REPLACE((SELECT org FROM mytable LIMIT 1), ",", "'),('"),"');");
PREPARE st FROM #str;
EXECUTE st;
SELECT
GROUP_CONCAT(DISTINCT CONCAT(SUBSTRING_INDEX(val, ':', 1), ":", (CAST(SUBSTRING_INDEX(val, ':', -1) AS UNSIGNED) + 1000)))
FROM temp;
Just make sure to replace SELECT org FROM mytable LIMIT 1 in the above query with your query that returns the string 1:38,25:39,41:101 you need to edit. Be aware that this example only shows how to process the value in one string. If you need to process values of multiple rows, you need to adjust a bit more...
Check sqlfiddle: http://sqlfiddle.com/#!9/bf6da4/1/0
i would like to replace value like:
1106,1107,1108
from select query to a string link like:
http://something.com/img/1/1/0/6/1106.jpg,http://something.com/img/1/1/0/7/1107.jpg,http://something.com/img/1/1/0/8/1108.jpg
can it be done in mysql query?
Assuming the image names have variable length, I think you'll need to write a stored function to implement this natively in MySQL, there's no obvious built-in function.
The example below will take 1106 and convert it to http://something.com/img/1/1/0/6/1106.jpg. To parse multiple image IDs like 1106,1107,1108 you'd need to extend it to insert the path again every time it finds a comma, or (better) select the results out of the database in a way that is not comma-separated.
DELIMITER //
CREATE FUNCTION TO_IMAGE_PATH(id VARCHAR(255), path VARCHAR(255))
RETURNS VARCHAR(255) DETERMINISTIC NO SQL
BEGIN
DECLARE output VARCHAR(255) DEFAULT path;
DECLARE position INT DEFAULT 1;
WHILE position <= LENGTH(id) DO
SET output = CONCAT(output, SUBSTRING(id, position, 1), '/');
SET position = position + 1;
END WHILE;
SET output = CONCAT(output, id, '.jpg');
RETURN output;
END//
DELIMITER ;
SELECT TO_IMAGE_PATH('1106', 'http://something.com/img/');
-- Output: http://something.com/img/1/1/0/6/1106.jpg
You might prefer to pass in the jpg extension, or hard-code the initial path.
While this does work, this seems like an example of a problem which might be better solved in another programming language after you have selected out your results.
If all of the image IDs are exactly 4 digits long, you could do the simpler (but less elegant)
SELECT CONCAT(
'http://something.com/img/',
SUBSTRING(field_name, 1, 1), '/',
SUBSTRING(field_name, 2, 1), '/',
SUBSTRING(field_name, 3, 1), '/',
SUBSTRING(field_name, 4, 1), '/',
field_name, '.jpg');
Again, you'd need to work out how to select the values out so they aren't comma-separated. In general, if you're storing values comma-separated in your database, then you shouldn't be.
Given the following strings which represent possible lists, how may I get an item at a specified index n
1,2,3,4,5
word1 word2 word3
pipe|delimited|list
Possible reasons for this functionality are
Extraction of specific elements from GROUP_CONCAT output
Extraction of specific elements from a SET column output (when cast to string)
Extraction of specific elements from a poorly normalised table containing a comma-separated list
Use within an iteration procedure to loop over a list and perform an action on each element within
There is no native function for this. You can use two SUBSTRING_INDEX functions. And you need to check if that specific index item exists:
SET #string:='1,2,3,4,5';
SET #delimiter:=',';
SET #n:=6;
SELECT
CASE WHEN
CHAR_LENGTH(#string)-CHAR_LENGTH(REPLACE(#string, #delimiter, ''))>=
#n*CHAR_LENGTH(#delimiter)-1
THEN
SUBSTRING_INDEX(SUBSTRING_INDEX(#string, #delimiter, #n), #delimiter, -1)
END;
SUBSTRING_INDEX(#string, #delimiter, #n) returns the substring from string #string before #n occurrences of the #delimiter.
SUBSTRING_INDEX( ... , #delimiter, -1) returns everything to the right of the final delimiter
you need to check if delimiter #n exists. We can substract the length of the string with the delimiter, and the string with the delimiter removed - using REPLACE(#string, #delimiter, '') - and see if it is greater than #n*CHAR_LENGTH(#delimiter)-1
Pure SQL way of doing it:-
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(somefield, '|', 3), '|', -1)
FROM sometable a
If you want to return NULL (or some other fixed value) if there is no 3rd element (for example):-
SELECT IF((LENGTH(somefield) - LENGTH(REPLACE(somefield, '|', '')) + 1) >= 10, SUBSTRING_INDEX(SUBSTRING_INDEX(somefield, '|', 10), '|', -1), NULL)
FROM sometable a
update totally forgot the SUBSTRING_INDEX -1 thing (pointed out by #fthiella and #Kickstart), so updated the examples below
Solved by creating a stored function which is able to be used in-line. The stored function is able to accept an input string, any single-character delimiter, and an index of the desired item to extract.
The stored function definition is as follows
CREATE DEFINER = `user`#`%`
FUNCTION `getListElement`(
inString VARCHAR(255) ,
inDelimiter CHAR(1) ,
inIndexToReturn TINYINT UNSIGNED
) RETURNS varchar(255) READS SQL DATA DETERMINISTIC SQL SECURITY INVOKER
BEGIN
-- Takes in as argument a string, and then breaks out the desired string
DECLARE resultString VARCHAR(255) DEFAULT inString;
DECLARE numberOfListElements TINYINT UNSIGNED DEFAULT 0;
DECLARE errorMessage VARCHAR(255) DEFAULT 'Requested index was < 1 which was invalid';
-- First of all, additional processing only needed for element 2 upwards
IF inIndexToReturn = 1 THEN
RETURN SUBSTRING_INDEX( resultString , inDelimiter , inIndexToReturn);
ELSEIF inIndexToReturn > 1 THEN
-- Count the number of elements
-- This will count the missing delimiters based off the replace. A list of 4 will be missing 3 characters.
SET numberOfListElements = ( CHAR_LENGTH( resultString ) + 1 ) - CHAR_LENGTH( REPLACE( resultString , inDelimiter , '' ) );
IF numberOfListElements >= inIndexToReturn THEN
-- Make sure to only return the last of the elements returend by the first SUBSTRING_INDEX
RETURN SUBSTRING_INDEX( SUBSTRING_INDEX( inString , inDelimiter , inIndexToReturn ) , inDelimiter , -1 );
END IF;
SET errorMessage = CONCAT('List index ',inIndexToReturn,' was requested from a list with ',numberOfListElements,' elements');
END IF;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;
END
In the examples above, the following could be used to extract specific list elements
SELECT getListElement( '1,2,3,4,5' , ',' , 2 ) returns 2
SELECT getListElement( REPLACE( 'word1 word2 word3' ,' ', ',' ) , ',' , 3 ) returns word3 (see note below for reason on the REPLACE)
SELECT getListElement( 'pipe|delimited|list' , '|' , 1 ) returns pipe
It would also be possible to use this within an iterator to loop over elements in a list. First of all you need to count the items in the list (see How to count items in comma separated list MySQL) however once you have that, it's just a matter of iterating over them as in this fragment from a stored procedure
-- Reinitialise variables
SET #list = '1,2,3,4,5';
SET #delimiter = ',';
SET #listLength = (CHAR_LENGTH( #list ) + 1 ) - CHAR_LENGTH( REPLACE( #list , #delimiter , '' ) );
SET #currentElement = 1;
listLoop: REPEAT
-- Select the list element and do something with it
SELECT getListElement( #list , #delimiter , #currentElement );
-- Increment the current element
SET #currentElement = #currentElement + 1;
UNTIL #currentElement > #listLength
END REPEAT listLoop;
important space-delimited lists seem to cause issues for this procedure, and so prior to parsing a string into the function I would recommend doing a simple REPLACE to replace the spaces with another suitable single-character separator (i.e. , or | depending on the content in the string)