How can i select only a part of a column ? I know I can use substring, but i need to select a string part of a column. As an example: Column can contain the following:
DIRL100
I need to select only the DIRL part in one column, and the 100 part as another.
I could do it with this specific column like so:
SELECT SUBSTRING(column, 5) AS part1,
SUBSTRING(column, 1, 4) AS part2 ....
But i cannot be sure that its always 4 letters (DIRL) before it gets numeric .. Can i somehow use REGEXP or something to extract only the numeric part and the letter part in each column ?
In other words.. Can i split a column by where the letters end. It could as an example contain DIRL100 or AB100200 which should be split into two columns each containing the letters from the column (DIRL or AB) and the digits from the column (100 or 100200) ?
Try this request:
SELECT LEFT(column, patindex('%[0-9]%', column)-1) AS Part1
, RIGHT(column, LEN(column) - patindex('%[0-9]%', column)+1) AS Part2
Unfortunately, the regexp functions in MySQL is limited. You have to write a custom function to help you.
DROP FUNCTION IF EXISTS get_index;
DELIMITER $$
CREATE FUNCTION get_index(targetString VARCHAR(255)) RETURNS INTEGER
BEGIN
DECLARE i INTEGER;
DECLARE min_index INTEGER;
DECLARE current_index INTEGER;
SET i = 0;
SET min_index = NULL;
loopNumbers: WHILE (i <= 9) DO
SET current_index = LOCATE(i, targetString);
IF current_index > 0 THEN
IF min_index IS NULL OR current_index < min_index THEN
SET min_index = current_index;
END IF;
END IF;
SET i = i + 1;
END WHILE;
RETURN(min_index);
END
$$
DELIMITER ;
What this function does is to obtain the first position of the numbers. Then using this function, you can modify your query to:
SELECT SUBSTRING(column_name FROM 1 FOR get_index(column_name)-1) AS first, SUBSTRING(column_name FROM get_index(column_name)) AS second
FROM table_name
WHERE column_name REGEXP '^[a-Z]+[0-9]+$'
The additional WHERE condition is optional to ensure that only data in the correct format is selected.
Related
Thanks to this answer https://stackoverflow.com/a/8180159/16349298 , i'm able to translate
a string into a temporary table (usable for WHERE <id> IN <tmpTable>.<colomn>)
The only modification i made is at the end (The select) :
CREATE PROCEDURE stringToTmpTable(IN inputString VARCHAR(255), IN sep VARCHAR(255))
BEGIN
declare pos int; -- Keeping track of the next item's position
declare item varchar(100); -- A single item of the input
declare breaker int; -- Safeguard for while loop
-- The string must end with the delimiter
if right(inputString, 1) <> sep then
set inputString = concat(inputString, sep);
end if;
DROP TABLE IF EXISTS MyTemporaryTable;
CREATE TEMPORARY TABLE MyTemporaryTable ( columnName varchar(100) );
set breaker = 0;
while (breaker < 2000) && (length(inputString) > 1) do
-- Iterate looking for the delimiter, add rows to temporary table.
set breaker = breaker + 1;
set pos = INSTR(inputString, sep);
set item = LEFT(inputString, pos - 1);
set inputString = substring(inputString, pos + 1);
insert into MyTemporaryTable values(item);
end while;
SELECT * FROM MyTemporaryTable;
END
I would like to use this process in a function or procedure in order to call it in any procedure that needs it.
So here is the problem :
I don't know how to store the result of this procedure into a variable : i can't use the SELECT * INTO #p FROM ...; like CALL stringToTmpTable(<string>,<separator>) INTO #table;
An other way would be to add OUT parameter to stringToTmpTable() but it can't return multiple rows. Unfortunatly the amount of parameters in the string is variable so i can't define as much variable as there is parameters in the string.
Finally the FIND_IN_SET() isn't the solution i need.
In the worst case I could copy / past the stringToTmpTable() process in any other procedure that needs it, but that doesn't seem like the best way to me.
Any suggestions ?
"i'm able to translate a string into a temporary table" too, but I am using a different method:
SET #input = 'Banana, Apple, Orange, Pears';
WITH RECURSIVE cte1 as (
select
#input as s,
substring_index(substring_index(#input,',',1),',',-1) as w,
length(#input)-length(replace(#input,',','')) x
union all
select
substring_index(s,',',-x),
trim(substring_index(substring_index(substring_index(s,',',-x),',',1),',',-1)) as w,
x-1 x
from cte1 where s<>'' and x>0
)
select * from cte1
DBFIDDLE
But it's a bit of a problem to determine the real problem you have, which is causing you to ask this question. So this is not an answer, but just a different way of selecting all words from a comma-delimted string.
I'm trying to make only the odd indexes of a string in uppercase (whereas the even indexes to be in lowercase) in MySQL.
For example: StackOverflow -> StAcKoVeRfLoW or hello -> HeLlO.
I found a way to this by extracting a letter at a time using the mid function, then concatenating based on which index the letter is at:
SET #x='hello';
SELECT #x as Initial,
Concat(ucase(mid(#x,1,1)),lcase(mid(#x,2,1)),ucase(mid(#x,3,1)),lcase(mid(#x,4,1)),ucase(mid(#x,5,1)))
as Final;
However I'm interested if there is a way to simplify this, since if the string would be larger then we would have some problems. So basically is there a way to modify it to something like:
Concat(ucase(mid(#x,odd index,1)),lcase(mid(#x,even index,1)))?
This is probably most simply done in your application, but can be achieved in MySQL. For MySQL 8+ you can use a recursive CTE to extract the individual letters from the string and GROUP_CONCAT to put them back together, changing the case on an alternating basis:
WITH RECURSIVE INITIAL AS (
SELECT 'StackOverflow' AS x
),
CTE AS (
SELECT 1 AS upper, SUBSTRING(x, 1, 1) AS letter, SUBSTRING(x, 2) AS remainder
FROM INITIAL
UNION ALL
SELECT 1 - upper, SUBSTRING(remainder, 1, 1), SUBSTRING(remainder, 2)
FROM CTE
WHERE LENGTH(remainder) > 0
)
SELECT GROUP_CONCAT(CASE WHEN upper THEN UPPER(letter) ELSE LOWER(letter) END SEPARATOR '') AS new
FROM CTE
Output:
StAcKoVeRfLoW
In versions lower than 8, you can use a user-defined function:
DELIMITER //
CREATE FUNCTION AlterCase(initial TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE l CHAR(1);
DECLARE new TEXT DEFAULT '';
WHILE i <= LENGTH(initial) DO
SET l = SUBSTRING(initial, i, 1);
SET new = CONCAT(new,
CASE WHEN i % 2 = 1 THEN UPPER(l) ELSE LOWER(l) END);
SET i = i + 1;
END WHILE;
RETURN new;
END //
DELIMITER ;
And call it as
SELECT AlterCase('StackOverflow')
Output:
StAcKoVeRfLoW
Note the function will work in MySQL 8+ too.
Demo on dbfiddle
I am trying to set a column with the code below:
declare before int;
set before = (
select X.delivery
from delivery as X
where X.delivery_id < NEW.delivery_id LIMIT 1
);
set NEW.delivery_col = before + 1;
My goal is to get one row before the new one and set the value plus 1;
If I set the variable with a number (Example: 2) it works but it is not working with the select.
Any idea where I am messing?
You can just use MAX() function to get the maximum value and add 1 to it, e.g.:
set before = SELECT MAX(delivery) FROM delivery;
set NEW.delivery_col = before + 1;
Or
set NEW.delivery_col = SELECT MAX(delivery) + 1 FROM delivery;
A couple of issues with the code you pasted above in the comment.
It doesn't add 1 to before.
You need to use a different delimiter when defining a trigger so the delimiters get included in the trigger.
Use back-tick marks to quote variable, column, and table names to avoid conflicts with reserved words
Give this a try:
DELIMITER |
CREATE TRIGGER `trigger_on_insert` BEFORE INSERT ON `delivery`
FOR EACH ROW
BEGIN
DECLARE `before` INT;
SET `before` = (SELECT X.`delivery` FROM `delivery` X WHERE X.`delivery_id` < NEW.`delivery_id` ORDER BY X.`delivery_id` DESC LIMIT 1);
SET NEW.`delivery_col` = `before` + 1;
END|
DELIMITER ;
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'