for example,
I have a dataset as below:
asdf1234asdf
1235asdfasdf
asdfasdfef489
How could I select such that I can get a result as below?
1234
1235
489
If your MySQL version supports it, create a function and use such as regex, to extract the first digits:
DELIMITER $$
CREATE FUNCTION first_digits(str TEXT)
RETURNS TEXT
BEGIN
DECLARE ret TEXT DEFAULT '';
DECLARE chr TEXT DEFAULT '';
DECLARE i INT DEFAULT 1;
WHILE i < (LENGTH(str) + 1) DO
SET chr = SUBSTRING(str, i, 1);
IF chr REGEXP '[0-9]'
THEN SET ret = CONCAT(ret, chr);
ELSEIF ret != ''
THEN RETURN ret;
END IF;
SET i = i + 1;
END WHILE;
RETURN ret;
END;
$$
DELIMITER ;
Then just SELECT desired column with newly created function first_digits();
SELECT first_digits('asdf1234asdf') num;
num
1234
To extract all digits (not only first), drop the ELSEIF part and rename the function such as digits.
To drop the function:
DROP FUNCTION IF EXISTS first_digits;
Hope this helps!
MySQL, unfortunately, doesn't have a good way of replacing regexes, but if you have only one numerical sequence, you could use trim:
SELECT TRIM(BOTH 'abcdefghijklmnopqrstuvwxyz' FROM word)
FROM (SELECT 'asdf1234asdf' AS word
UNION ALL
SELECT '1235asdfasdf'
UNION ALL
SELECT 'asdfasdfef489') t
Related
I need to sort below cell values using mysql
Example:
cell contain red,blue,green
But I want that in alphabetic order.
Steps to do this,
1.First you need to make a procedure call for sorting values
2.Call your procedure then
Here is the code to create mysql procedure
-- sort comma separated substrings with unoptimized bubble sort
DROP FUNCTION IF EXISTS sortString;
DELIMITER |
CREATE FUNCTION sortString(inString TEXT) RETURNS TEXT
BEGIN
DECLARE delim CHAR(1) DEFAULT ','; -- delimiter
DECLARE strings INT DEFAULT 0; -- number of substrings
DECLARE forward INT DEFAULT 1; -- index for traverse forward thru substrings
DECLARE backward INT; -- index for traverse backward thru substrings, position in calc. substrings
DECLARE remain TEXT; -- work area for calc. no of substrings
-- swap areas TEXT for string compare, INT for numeric compare
DECLARE swap1 TEXT; -- left substring to swap
DECLARE swap2 TEXT; -- right substring to swap
SET remain = inString;
SET backward = LOCATE(delim, remain);
WHILE backward != 0 DO
SET strings = strings + 1;
SET backward = LOCATE(delim, remain);
SET remain = SUBSTRING(remain, backward+1);
END WHILE;
IF strings < 2 THEN RETURN inString; END IF;
REPEAT
SET backward = strings;
REPEAT
SET swap1 = SUBSTRING_INDEX(SUBSTRING_INDEX(inString,delim,backward-1),delim,-1);
SET swap2 = SUBSTRING_INDEX(SUBSTRING_INDEX(inString,delim,backward),delim,-1);
IF swap1 > swap2 THEN
SET inString = TRIM(BOTH delim FROM CONCAT_WS(delim
,SUBSTRING_INDEX(inString,delim,backward-2)
,swap2,swap1
,SUBSTRING_INDEX(inString,delim,(backward-strings))));
END IF;
SET backward = backward - 1;
UNTIL backward < 2 END REPEAT;
SET forward = forward +1;
UNTIL forward + 1 > strings
END REPEAT;
RETURN inString;
END |
DELIMITER ;
To make procedure call just you have to use,
-- example call:
SET #Xstr = "red,blue,green"; // for query purpose only you need to write within (SQL Query here for that row)
SELECT sortString(#Xstr) AS s1
Please see the documentation guide map
Click here to read
Also there is an alternative way to do if you are interested to study is that about FIND_IN_SET, please you can find some idea from one of the question from stackoverflow. Click here to read
You can create a function which sorts the items in the column:
create function f_comma_list_order ( t text )
returns text
begin
declare v_c int;
drop temporary table if exists tmp;
create temporary table tmp ( v text );
set v_c = 1;
while( v_c > 0 ) do
select locate(',', t) into v_c;
if (v_c>0) then
insert into tmp select substr(t, 1, v_c-1);
set t = substr(t, v_c+1);
else
insert into tmp values (t);
end if;
end while;
select group_concat(v order by v) into t
from tmp;
return t;
end
and then call the function:
select f_comma_list_order('red,green,blue')
can anybody help me with my sorting function - seriously I don't know how can I make it work as supposed to. :( Database is in MariaDB in Xampp. I use phpMyAdmin to execute the query.
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeral$$
CREATE FUNCTION convRomanNumeral (numeral CHAR(4))
RETURNS INT
BEGIN
DECLARE intnum INT;
CASE numeral
WHEN "I" THEN intnum = 1;
WHEN "II" THEN intnum = 2;
END CASE;
RETURN intnum;
END;
$$
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-14') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 141 ORDER BY convRomanNumeral(SUBSTRING(SUBSTRING_INDEX(title,'/',1),' ',-2) ASC, SUBSTRING_INDEX(title,'/',-2)+0 ASC;
So what I want to achieve is to sort documents by title. Example titles are:
Document Nr I/36/2006
Document Nr II/36/2006
Document Nr I/32/2006
Document Nr II/19/2006
After sorting them by first Roman number and then by second Arabic number I want to update the date. Code below for updating by only second Arabic number works properly:
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-14') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 141 ORDER BY SUBSTRING_INDEX(title,'/',-2)+0 ASC;
I would like to use CASE to return proper variable for Roman values. I know it's not perfect but I can't even make the CASE and FUNCTION work. What I am doing wrong? All suggestions are welcome.
The best way to do this is to add another column that has a sortable equivalent of that string. And use non-SQL code to do the parsing and building of that column before inserting into the table.
First mistake that I was making it was trying to execute the whole query at once... After taking the first lodge out of the way the debugging seemed way simpler. :D
So I created my case function to convert Roman numerals:
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeralSubFunction$$
CREATE FUNCTION convRomanNumeralSubFunction (numeral CHAR(1))
RETURNS INT
BEGIN
DECLARE intnum INT;
CASE numeral
WHEN "I" THEN SELECT 1 INTO intnum;
WHEN "X" THEN SELECT 10 INTO intnum;
WHEN "C" THEN SELECT 100 INTO intnum;
WHEN "M" THEN SELECT 1000 INTO intnum;
WHEN "V" THEN SELECT 5 INTO intnum;
WHEN "L" THEN SELECT 50 INTO intnum;
WHEN "D" THEN SELECT 500 INTO intnum;
END CASE;
RETURN intnum;
END;
$$
After that I declared the second function needed for conversion. I don't know if You can declare function inside function... and I didn't want to waste more time on this. For sure You can declare Function inside Procedure. Anyhow. WARNING: This function is not proof of BAD numerals like IIX. Numerals like that or will be badly counted. Also AXI will not count.
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeral$$
CREATE FUNCTION convRomanNumeral (numeral CHAR(10))
RETURNS INT
BEGIN
DECLARE currentintnum, previntnum, intnum, counter, numerallength INT;
SET numerallength = LENGTH(numeral);
SET counter = numerallength;
SET intnum = 0;
SET previntnum = 0;
WHILE counter > 0 DO
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as integer);
IF currentintnum < previntnum THEN
SET intnum = intnum - currentintnum;
ELSE
SET intnum = intnum + currentintnum;
END IF;
SET previntnum = currentintnum;
SET counter = counter - 1;
END WHILE;
RETURN intnum;
END;
$$
So that's it. Now You can convert all kind of Roman numerals and sort them up.
Use this to test the conversion:
SELECT convRomanNumeral("XIX");
This is example sorting code that I in the end used:
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-07') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 67 ORDER BY convRomanNumeralBreak(SUBSTRING_INDEX(SUBSTRING_INDEX(title,'/',1),' ',-1)) ASC, SUBSTRING_INDEX(title,'/',-2)+0 ASC;
Also one more thing - if You'll try to excecute this on mySQL then You have to fix this line:
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as integer);
into this:
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as SIGNED);
This code could be improved but as the #Rick James stated this should be done differently - not in as db update but in different table structure and sorting mechanism.
I am trying to run a query that can find all the records from a field contains the letters.
For example suppose a state field contains a record value "New York" and another record conatains NY. Now i am searching for NY or New york will return these 2 records. What will be the query.
Currently i am using
like %New York%" or "%NY%"
Any suggestion
No your query is not correct as it searches for anything containing New York or NY.
So if there is PENNY that will be matched although it shouldn't be....
Your query must be something like this.
SELECT * from TABLE where field in ('NEW YORK','NY')
Now to fetch acronym,you can use
delimiter $$
drop function if exists `initials`$$
CREATE FUNCTION `initials`(str text, expr text) RETURNS text CHARSET utf8
begin
declare result text default '';
declare buffer text default '';
declare i int default 1;
if(str is null) then
return null;
end if;
set buffer = trim(str);
while i <= length(buffer) do
if substr(buffer, i, 1) regexp expr then
set result = concat( result, substr( buffer, i, 1 ));
set i = i + 1;
while i <= length( buffer ) and substr(buffer, i, 1) regexp expr do
set i = i + 1;
end while;
while i <= length( buffer ) and substr(buffer, i, 1) not regexp expr do
set i = i + 1;
end while;
else
set i = i + 1;
end if;
end while;
return result;
end$$
drop function if exists `acronym`$$
CREATE FUNCTION `acronym`(str text) RETURNS text CHARSET utf8
begin
declare result text default '';
set result = initials( str, '[[:alnum:]]' );
return result;
end$$
delimiter ;
So,your final query will be something like this.
SELECT * from TABLE where field in ('NEW YORK',select acronym('Come Again? That Cant Help!'))
Source:- Mysql extract first letter of each word in a specific column
Presumably, the logic that you want is:
col like '%New York%' or col like '%NY%'
or, if you want to use regular expressions:
col regexp 'New York|NY'
Select * from table where col like '%N' or col like '%n'
I wrote a function to replace first occurence in MySQL Text colum, but it's a little bit complicated...
UPDATE
table_name
SET
column=CONCAT(
LEFT(column,LOCATE('some string', column)-1),
CONCAT(substring(column, LOCATE('some string', column) + $length),
'new string'))
Where $length is length of string, that we want to replace. If we use php it is strlen() function but in MySQL it would be CHAR_LENGTH() function.
Do you know better way to replace only first match in text columns ?
You could use TRIM:
UPDATE table_name SET column = TRIM(LEADING 'some string' FROM column);
assuming 'some string' does not have more than 1 consecutive occurrence at the start of the contents of 'column'.
So, it would work if column contained:
"some string foo some string"
but not for:
"some string some string foo some string"
Edit - Added MySQL function to simplify process
I can't see an alternative to the mechanism you are using, but executing it could be simplified by creating a function in MySQL (if you have the privilege):
delimiter $$
create function replace_first(
p_text_to_search varchar(255),
p_text_to_replace varchar(255)
)
returns varchar(255)
begin
declare v_found_pos int(11);
declare v_found_len int(11);
declare v_text_with_replacement varchar(255);
select locate(p_text_to_replace, p_text_to_search)
into v_found_pos;
select char_length(p_text_to_replace)
into v_found_len;
select concat(
left(p_text_to_search, v_found_pos-1),
mid(p_text_to_search, (v_found_pos + v_found_len))
)
into v_text_with_replacement;
return v_text_with_replacement;
end$$
delimiter ;
then you can call it using:
select replace_first('bar foo foo baz foo', 'foo');
result:
'bar foo baz'
I have created function can replace any index of text:
/************** REPLACE_TEXT_OF_INDEX ***************/
DROP FUNCTION IF EXISTS REPLACE_TEXT_OF_INDEX;
DELIMITER $$
CREATE FUNCTION REPLACE_TEXT_OF_INDEX(_text VARCHAR(3072), _subText VARCHAR(1024), _toReplaceText VARCHAR(1024), _index INT UNSIGNED)
RETURNS VARCHAR(3072)
BEGIN
DECLARE _prefixText, _sufixText VARCHAR(3072);
DECLARE _starIndex INT;
DECLARE _loopIndex, _textIndex INT UNSIGNED DEFAULT 0;
IF _text IS NOT NULL AND LENGTH(_text) > 0 AND
_subText IS NOT NULL AND LENGTH(_subText) > 0 AND
_toReplaceText IS NOT NULL AND _index > 0 THEN
WHILE _loopIndex < LENGTH(_text) AND _textIndex < _index DO
SELECT LOCATE(_subText, _text, _loopIndex + 1) INTO _loopIndex;
IF _loopIndex > 0 THEN
SET _textIndex = _textIndex + 1;
ELSE
SET _loopIndex = LENGTH(_text) + 1;
END IF;
END WHILE;
IF _textIndex = _index THEN
SELECT LOCATE(_subText, _text, _loopIndex) INTO _starIndex;
SELECT SUBSTRING(_text, 1, _starIndex -1) INTO _prefixText;
SELECT SUBSTRING(_text, _starIndex + LENGTH(_subText), LENGTH(_text)) INTO _sufixText;
RETURN CONCAT(_prefixText, _toReplaceText, _sufixText);
END IF;
END IF;
RETURN _text;
END;
$$
DELIMITER ;
SELECT REPLACE_TEXT_OF_INDEX('WORD1 WORD2 WORD3 WORD4 WORD5', 'WORD', '*',1);
I have a table with two columns: price (int) and price_display (varchar).
price is the actual numerical price, e.g. "9990"
price_display is the visual representation, e.g. "$9.99" or "9.99Fr"
I've been able to confirm the two columns match via regexp:
price_display not regexp
format(price/1000, 2)
But in the case of a mismatch, I want to extract the value from the price_display column and set it into the price column, all within the context of an update statement. I've not been able to figure out how.
Thanks.
This function does the job of only returning the digits 0-9 from the string, which does the job nicely to solve your issue, regardless of what prefixes or postfixes you have.
http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#815
Copied here for reference:
SET GLOBAL log_bin_trust_function_creators=1;
DROP FUNCTION IF EXISTS digits;
DELIMITER |
CREATE FUNCTION digits( str CHAR(32) ) RETURNS CHAR(32)
BEGIN
DECLARE i, len SMALLINT DEFAULT 1;
DECLARE ret CHAR(32) DEFAULT '';
DECLARE c CHAR(1);
IF str IS NULL
THEN
RETURN "";
END IF;
SET len = CHAR_LENGTH( str );
REPEAT
BEGIN
SET c = MID( str, i, 1 );
IF c BETWEEN '0' AND '9' THEN
SET ret=CONCAT(ret,c);
END IF;
SET i = i + 1;
END;
UNTIL i > len END REPEAT;
RETURN ret;
END |
DELIMITER ;
SELECT digits('$10.00Fr');
#returns 1000
One approach would be to use REPLACE() function:
UPDATE my_table
SET price = replace(replace(replace(price_display,'Fr',''),'$',''),'.','')
WHERE price_display not regexp format(price/1000, 2);
This works for the examples data you gave:
'$9.99'
'9.99Fr'
Both result in 999 in my test. With an update like this, it's important to be sure to back up the database first, and be cognizant of the formats of the items. You can see all the "baddies" by doing this query:
SELECT DISTINCT price_display
FROM my_table
WHERE price_display not regexp format(price/1000, 2)
ORDER BY price_display;
For me CASTING the field did the trick:
CAST( price AS UNSIGNED ) // For positive integer
CAST( price AS SIGNED ) // For negative and positive integer
IF(CAST(price AS UNSIGNED)=0,REVERSE(CAST(REVERSE(price) AS UNSIGNED)),CAST(price AS UNSIGNED)) // Fix when price starts with something else then a digit
For more details see:
https://dev.mysql.com/doc/refman/5.0/en/cast-functions.html
This is a "coding horror", relational database schemas should NOT be written like this!
Your having to write complex and unnecessary code to validate the data.
Try something like this:
SELECT CONCAT('$',(price/1000)) AS Price FROM ...
In addition, you can use a float, double or real instead of a integer.
If you need to store currency data, you might consider adding a currency field or use the systems locale functions to display it in the correct format.
I create a procedure that detect the first number in a string and return this, if not return 0.
DROP FUNCTION IF EXISTS extractNumber;
DELIMITER //
CREATE FUNCTION extractNumber (string1 VARCHAR(255)) RETURNS INT(11)
BEGIN
DECLARE position, result, longitude INT(11) DEFAULT 0;
DECLARE string2 VARCHAR(255);
SET longitude = LENGTH(string1);
SET result = CONVERT(string1, SIGNED);
IF result = 0 THEN
IF string1 REGEXP('[0-9]') THEN
SET position = 2;
checkString:WHILE position <= longitude DO
SET string2 = SUBSTR(string1 FROM position);
IF CONVERT(string2, SIGNED) != 0 THEN
SET result = CONVERT(string2, SIGNED);
LEAVE checkString;
END IF;
SET position = position + 1;
END WHILE;
END IF;
END IF;
RETURN result;
END //
DELIMITER ;
Return last number from the string:
CREATE FUNCTION getLastNumber(str VARCHAR(255)) RETURNS INT(11)
DELIMETER //
BEGIN
DECLARE last_number, str_length, position INT(11) DEFAULT 0;
DECLARE temp_char VARCHAR(1);
DECLARE temp_char_before VARCHAR(1);
IF str IS NULL THEN
RETURN -1;
END IF;
SET str_length = LENGTH(str);
WHILE position <= str_length DO
SET temp_char = MID(str, position, 1);
IF position > 0 THEN
SET temp_char_before = MID(str, position - 1, 1);
END IF;
IF temp_char BETWEEN '0' AND '9' THEN
SET last_number = last_number * 10 + temp_char;
END IF;
IF (temp_char_before NOT BETWEEN '0' AND '9') AND
(temp_char BETWEEN '0' AND '9') THEN
SET last_number = temp_char;
END IF;
SET position = position + 1;
END WHILE;
RETURN last_number;
END//
DELIMETER;
Then call this functions:
select getLastNumber("ssss111www222w");
print 222
select getLastNumber("ssss111www222www3332");
print 3332