trigger validation using regular expression not working for newline character - mysql

I am trying to validate inserts using a mysql-trigger. The inserts in col1 should be only numerical. It is NOT an option to define col1 as an integer in the first place. However, the validation fails if there is a newline character at the end.
Heres the table
CREATE TABLE testTable(col1 varchar(20), col2 int);
and here the trigger validator
DELIMITER $$
CREATE TRIGGER check_numeric BEFORE INSERT ON testTable
FOR EACH ROW
BEGIN
IF (NEW.col1 REGEXP '^[0-9]+$') = 0 THEN
SIGNAL SQLSTATE '12345' SET MESSAGE_TEXT='non-numerical';
END IF;
END $$
If I try to insert a string including a newline character at the end this passes the validation, although it shouldn't in my opinion as the newline character is not numeric.
INSERT INTO testTable values('323423\n', 12);
Does anybody have an idea what I am missing here?

Another option is to search for anything that is not a digit instead, this will return 1 if any character is not a digit: REGEXP '[^[:digit:]]'
So the IF clause would be
IF (NEW.col1 REGEXP '[^[:digit:]]') = 1 THEN ...
Of course you could write [^0-9] instead of [:digit:] if you don't want to use the character class

can you check in this way
IF NEW.col1 REGEXP '^[0-9]+$' AND NEW.col1 NOT REGEXP '\n' THEN
with w as
(select '1234' c1 from dual union all
select '1234\n' from dual)
select * from w
where c1 REGEXP '^[0-9]+$' AND c1 NOT REGEXP "\n";
Result : 1234
Working Fiddle

Related

MySQL Select only part of column

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.

How to separate a string and re build it

Separating String list and replacing same list with new values in mysql
I have following data in my table_1 Table
table_1(Currently saved structure)
code value
12_A ["A","B","C","D"]
12_B ["E","F","G","H"]
12_3 ["I","J","K","L"]
But each code have different values with different description. like::
code value description
12_A A Apple
12_A B Ball
12_A C Cat
12_A D Dog
12_B E Eagle
12_B F Flag
. . .
. . .
. . .
I have to Separate the value list from table_1 and
need to save again in same table i.e table_1(in this structure)::
code value
12_A ["Apple","Ball","Cat","Dog"]
12_B ["Eagle","Flag",.......]
12_3 [......................]
You can use GROUP_CONCAT() :
UPDATE Table1 s
SET s.value = (SELECT t.code,CONCAT('["',
GROUP_CONCAT(t.description ORDER BY t.description SEPARATOR '","'),
']')
FROM Table_With_val t
WHERE t.code = s.code
AND s.value LIKE CONCAT('%"',t.value,'"%'))
You didn't provide any conclusive information, I assumed the second data sample you provided is an existing table, and table1 is the table you want to update.
NOTE: This is a bad DB structure! it would most defiantly cause problem in the future especially when required to make joins . I strongly advise you to normalize your data and store each description and value in its own record.
you can create a function in which you can pass your string list as parameter in case of your example ["A","B","C","D"] will be the parameter. The function will break down the string and will concatenate the descriptions according. The example of the function you can use is given below:
DELIMITER $$
DROP FUNCTION IF EXISTS codeToDesc$$
CREATE FUNCTION codeToDesc(commaSeperatedCodeList TEXT) RETURNS TEXT CHARSET utf8
BEGIN
DECLARE finalString TEXT;
DECLARE inputCodeList TEXT;
DECLARE codeName VARCHAR(255);
DECLARE codecount BIGINT(5);
SET finalString='';
SET inputCodeList = REPLACE(REPLACE(REPLACE(commaSeperatedCodeList,'[',''),']',''),'"','');
DROP TEMPORARY TABLE IF EXISTS test.code_table;
DROP TEMPORARY TABLE IF EXISTS test.code_count;
CREATE TEMPORARY TABLE test.code_table (CODE VARCHAR(255));
CREATE TEMPORARY TABLE test.code_count (countNo BIGINT(11));
INSERT INTO test.code_count(countNo) SELECT(LENGTH(inputCodeList)-LENGTH(REPLACE(inputCodeList,',','')) + 1);
BEGIN
DECLARE table_cursor CURSOR FOR SELECT countNo FROM test.code_count;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET codecount = (SELECT countNo FROM test.code_count ORDER BY countNo ASC LIMIT 1);
OPEN table_cursor;
readLoop1: LOOP
FETCH table_cursor INTO codecount;
IF codecount=0 THEN
LEAVE readLoop1;
END IF;
SET codeName=(SELECT SUBSTRING_INDEX(inputCodeList,',',1));
INSERT INTO test.code_table(CODE) SELECT codeName;
SET inputCodeList=(SELECT TRIM(BOTH ',' FROM REPLACE(inputCodeList,codeName,'')));
INSERT INTO test.code_count(countNo) SELECT codecount-1;
SET codeName='';
END LOOP;
CLOSE table_cursor;
END;
-- use your code and description here, i guess those should be fixed
SELECT CONCAT('["',REPLACE(GROUP_CONCAT(CASE WHEN CODE='A' THEN 'Apple'
WHEN CODE = 'B' THEN 'Ball'
WHEN CODE = 'C' THEN 'Cat'
WHEN CODE = 'D' THEN 'Dog'
ELSE '' END),',','","'),'"]') INTO finalString FROM test.code_table;
RETURN finalString;
END$$
DELIMITER ;
Try this, let me know if you any issue occurred.

Difficulty of removing the apostrophe in MySQL stored procedure

I am trying to remove apostrophes, below is the whole procedure.
BEGIN
SET _myparam = replace(_myparam, "'", '');
UPDATE `Table` SET NAME=_myparam WHERE UID=_someotherparam;
END
I also tried:
SET _myparam = replace(_myparam, '\'', '');
SET _myparam = replace(_myparam, '''', '');
SET _myparam = replace(_myparam, CHAR(39), '');
SET _myparam = replace(_myparam, '%\'%', '');
This seems to work with any other character but not the apostrophe. Nothing here seems to remove the apostrophe character. Any help would be appreciated.
I Googled everything I could think of, and have been pulling out hair for hours. Thanks!
I just tested this standard select out and it works. have you just tried
'\'' ?
ie, this works in standard select:
SELECT REPLACE("hi th'ere", '\'', '');
this does not:
SELECT REPLACE("hi th'ere",'%\'%', '');
of course this also works:
SELECT REPLACE("hi th'ere","'", '');
Edit: Here is the solution as MySQL Stored Procedure.
DELIMITER //
CREATE PROCEDURE TestStoredProc(IN _myvariable VARCHAR(25))
BEGIN
SET _myvariable = REPLACE(_myvariable, '\'', '');
SELECT _myvariable;
END //
DELIMITER ;
and
CALL TestStoredProc("hi th'ere");
You can also use two single-quotes which means one literal single-quote in standard SQL.
REPLACE("I wan't to be alone", '''', '')
FYI, in standard SQL, double-quotes are for delimiting identifiers like table names and column names. Single quotes are for delimiting strings and dates. It's a non-standard MySQLism to treat double-quotes as the same as single-quotes. But I use it in the example above, so I don't have to escape the apostrophe.
I also tested:
USE test;
DROP TABLE IF EXISTS `Table`;
CREATE TABLE `Table` (
UID INT PRIMARY KEY,
NAME TEXT
);
INSERT INTO `Table` VALUES (1, 'foo');
DROP PROCEDURE IF EXISTS q;
DELIMITER !!
CREATE PROCEDURE q(_myparam TEXT, _someotherparam INT)
BEGIN
SET _myparam = REPLACE(_myparam, '''', '');
UPDATE `Table` SET NAME=_myparam WHERE UID=_someotherparam;
END!!
DELIMITER ;
CALL q("I wan't to be alone", 1);
SELECT * FROM `Table`;
+-----+--------------------+
| UID | NAME |
+-----+--------------------+
| 1 | I want to be alone |
+-----+--------------------+
It seems to work just fine. I also tested using VARCHAR(20) for _myparam and that works too.

Remove trailing zeros in decimal value with changing length

I have a procedure I am doing that displays odds but the client wants only significant digits to be shown. So, 1.50 would show as '1.5' and 1.00 would show as '1'.
How can I get MySQL to not display trailing zeros;
i.e. in the database:
Odds
1.500
23.030
2.000
4.450
would display as
1.5
23.03
2
4.45
Thanks for any help
Easiest way by far, just add zero!
Examples:
SET
#yournumber1="1.500",
#yournumber2="23.030",
#yournumber3="2.000",
#yournumber4="4.450"
;
SELECT
(#yournumber1+0),
(#yournumber2+0),
(#yournumber3+0),
(#yournumber4+0)
;
+------------------+------------------+------------------+------------------+
| (#yournumber1+0) | (#yournumber2+0) | (#yournumber3+0) | (#yournumber4+0) |
+------------------+------------------+------------------+------------------+
| 1.5 | 23.03 | 2 | 4.45 |
+------------------+------------------+------------------+------------------+
1 row in set (0.00 sec)
If the column your value comes from is DECIMAL or NUMERIC type, then cast it to string first to make sure the conversion takes place...ex:
SELECT (CAST(`column_name` AS CHAR)+0) FROM `table_name`;
For a shorter way, just use any built-in string function to do the cast:
SELECT TRIM(`column_name`)+0 FROM `table_name`;
EDIT: I would use the answer below by Christopher McGowan instead - adding 0 to the value, which is better, instead.
It's important to check there is actually a decimal point if doing trimming.
So I think you'd want to use:
SELECT TRIM(TRAILING '.' FROM TRIM(TRAILING '0' from yourfield)) AS yourfield
FROM yourtable
WHERE yourfield LIKE '%.%'
this worked for me.. round the field to 2 decimal places and then trim any trailing zeros
So that 2.10 is 2.1
SELECT trim(round(FIELDNAME,2))+0
FROM tbl_name
....
To remove trailing zeros from a DECIMAL/NUMERIC or string type column, you can simply cast the value to DOUBLE, e.g.:
SELECT CAST(mycol AS DOUBLE) from mytable;
or
SELECT mycol + 0E0 FROM mytable;
In fact, the "cast to char and add zero" trick mentioned in other answers does the same, but in a more indirect (and likely less efficient) way, e.g:
SELECT CAST(mycol AS CHAR)+0 FROM mytable; -- converts to string, then to number
SELECT TRIM(mycol)+0 FROM mytable; -- ditto
Please use below function , it will take care of number having zero without decimal places i.e 150 etc....
SET #saved_cs_client = ##character_set_client;
SET character_set_client = utf8;
DELIMITER $$
USE `mydbname`$$
DROP FUNCTION IF EXISTS `FN_STRIP_TRAILING_ZER0`$$
CREATE DEFINER=`mydbuser`#`%` FUNCTION `FN_STRIP_TRAILING_ZER0`(tNumber DECIMAL(10,7)) RETURNS VARCHAR(20) CHARSET utf8
BEGIN
DECLARE strBuff VARCHAR(20);
DECLARE cnt NUMERIC(2);
DECLARE tString VARCHAR(20);
SELECT CAST(tNumber AS CHAR) INTO tString;
SELECT LOCATE('.',tString) INTO cnt;
IF cnt > 0 THEN
SELECT TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM tString)) INTO strBuff;
ELSE
SET strBuff = tString;
END IF;
RETURN strBuff;
END$$
DELIMITER ;
SET character_set_client = #saved_cs_client;
Addendum:
Typically to call this would involve:
SELECT FN_STRIP_TRAILING_ZER0(1.5);
The best solution I found is to cast your round value to FLOAT:
SELECT CAST(ROUND(1.2345984372, 2) AS FLOAT)
Here's what worked for me:
SINGLE COLUMN:
SELECT TRIM(column_name)+0 AS column_name FROM table_name;
MULTIPLE COLUMNS:
SELECT
TRIM(column1)+0 AS column1,
TRIM(column2)+0 AS column2,
TRIM(column3)+0 AS column3,
FROM table_name;
Taking forward the answer provided by #fooquency, if the column is already declared as a DECIMAL with a non-zero value for D in DECIMAL(M, D), we do not need to perform the WHERE condition
WHERE yourfield LIKE '%.%'
as the values in the column will always contain D digits after the decimal dot (.)
If you are using PHP as the scripting language you may use the following:
$var = (float)$var_having_extra_0; // $var = (float) 17.5000
Or use the PHP floatval function:
$var = floatval($var_having_extra_0); // $var = floatval(17.5000)
Using ROUND or CEILING, in the query you just have to type:
SELECT ROUND(2/50)
or
SELECT CEILING(2/50)
I had a similar problem in a situation where I could not modify the code nor the SQL query, but I was allowed to modify the database structure. So I changed the column format from DECIMAL to FLOAT and it solved my problem.
SELECT TRIM(TRAILING '.' FROM TRIM(TRAILING '0' from yourfield)) AS yourfield
FROM yourtable
WHERE yourfield LIKE '%.%'
or
SELECT TRIM(TRAILING '.' FROM TRIM(TRAILING '0' from yourfield)) AS yourfield
FROM yourtable
WHERE instr(yourfield,'.') != 0
work ok but require a "where" clause.
I think the best solution is probably:
SELECT TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM ROUND(yourfield,3)))
FROM yourtable
as it doesn't require a "where" clause, doesn't require any special code,
and also lets you set the maximum precision of the number upfront.
Taking fragments of the others answers in this page I came to this conclusion:
SELECT ( IF(
myfield LIKE '%.%',
TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM myfield)),
myfield
) ) FROM mytable
Cheers
SELECT TRIM(TRAILING '0' FROM yourodds)
FROM ...
Docs for the TRIM function.

MySQL find_in_set with multiple search string

I find that find_in_set only search by a single string :-
find_in_set('a', 'a,b,c,d')
In the above example, 'a' is the only string used for search.
Is there any way to use find_in_set kind of functionality and search by multiple strings, like :-
find_in_set('a,b,c', 'a,b,c,d')
In the above example, I want to search by three strings 'a,b,c'.
One way I see is using OR
find_in_set('a', 'a,b,c,d') OR find_in_set('b', 'a,b,c,d') OR find_in_set('b', 'a,b,c,d')
Is there any other way than this?
there is no native function to do it, but you can achieve your aim using following trick
WHERE CONCAT(",", `setcolumn`, ",") REGEXP ",(val1|val2|val3),"
The MySQL function find_in_set() can search only for one string in a set of strings.
The first argument is a string, so there is no way to make it parse your comma separated string into strings (you can't use commas in SET elements at all!). The second argument is a SET, which in turn is represented by a comma separated string hence your wish to find_in_set('a,b,c', 'a,b,c,d') which works fine, but it surely can't find a string 'a,b,c' in any SET by definition - it contains commas.
You can also use this custom 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, '');
DELIMITER $$
CREATE FUNCTION `FIND_SET_EQUALS`(`s1` VARCHAR(200), `s2` VARCHAR(200))
RETURNS TINYINT(1)
LANGUAGE SQL
BEGIN
DECLARE a INT Default 0 ;
DECLARE isEquals TINYINT(1) Default 0 ;
DECLARE str VARCHAR(255);
IF s1 IS NOT NULL AND s2 IS NOT NULL THEN
simple_loop: LOOP
SET a=a+1;
SET str= SPLIT_STR(s2,",",a);
IF str='' THEN
LEAVE simple_loop;
END IF;
#Do check is in set
IF FIND_IN_SET(str, s1)=0 THEN
SET isEquals=0;
LEAVE simple_loop;
END IF;
SET isEquals=1;
END LOOP simple_loop;
END IF;
RETURN isEquals;
END;
$$
DELIMITER ;
SELECT FIND_SET_EQUALS('a,c,b', 'a,b,c')- 1
SELECT FIND_SET_EQUALS('a,c', 'a,b,c')- 0
SELECT FIND_SET_EQUALS(null, 'a,b,c')- 0
Wow, I'm surprised no one ever mentioned this here.In a nutshell, If you know the order of your members, then just query in a single bitwise operation.
SELECT * FROM example_table WHERE (example_set & mbits) = mbits;
Explanation:
If we had a set that has members in this order: "HTML", "CSS", "PHP", "JS"... etc.
That's how they're interpreted in MySQL:
"HTML" = 0001 = 1
"CSS" = 0010 = 2
"PHP" = 0100 = 4
"JS" = 1000 = 16
So for example, if you want to query all rows that have "HTML" and "CSS" in their sets, then you'll write
SELECT * FROM example_table WHERE (example_set & 3) = 3;
Because 0011 is 3 which is both 0001 "HTML" and 0010 "CSS".
Your sets can still be queried using the other methods like REGEXP , LIKE, FIND_IN_SET(), and so on. Use whatever you need.
Amazing answer by #Pavel Perminov! - And also nice comment by #doru for dynamically check..
From there what I have made for PHP code CONCAT(',','" . $country_lang_id . "', ',') REGEXP CONCAT(',(', REPLACE(YourColumnName, ',', '|'), '),') this below query may be useful for someone who is looking for ready code for PHP.
$country_lang_id = "1,2";
$sql = "select a.* from tablename a where CONCAT(',','" . $country_lang_id . "', ',') REGEXP CONCAT(',(', REPLACE(a.country_lang_id, ',', '|'), '),') ";
You can also use the like command for instance:
where setcolumn like '%a,b%'
or
where 'a,b,c,d' like '%b,c%'
which might work in some situations.
you can use in to find match values from two values
SELECT * FROM table WHERE myvals in (a,b,c,d)