MySQL search array without PHP - mysql

I want to search an array in MySQL without PHP but all the examples I found were with PHP. Here is my procedure:
BEGIN
DECLARE indx INT default 0;
DECLARE y INT default 0;
UPDATE radios SET geoblocking_enabled = in_enabledgeoblocking WHERE id = in_radioid;
DELETE FROM geoblocking WHERE assigned_radioid = in_radioid;
SELECT JSON_EXTRACT(in_geoblockingjson ,'$.countries');
SET indx = JSON_LENGTH(JSON_EXTRACT(in_geoblockingjson ,'$.countries'));
REPEAT
INSERT INTO geoblocking (country, countryindex) VALUES (JSON_EXTRACT(in_geoblockingjson, CONCAT("$[", indx, "].countries")), JSON_EXTRACT(in_geoblockingjson, CONCAT("$[", indx, "].countryindexes")));
SET y = y + 1;
UNTIL y = indx
END REPEAT;
END
For some reason it inserts the data but with NULL values.. here is what I used for my example request:

Related

mysql search comma list of number ranges

In one of my tables fields I contain age ranges. They would be in a format such as
27-51,18-28,10-17
37-55,60-70
1-5,11-16,30-32,60-90
etc
I'm trying to build a SELECT statement where I can search if my given age falls into any of the ranges... something such as
SELECT * from table where age IN (1-5,11-16,30-32,60-90)
However it would search within the given ranges
I can do it if I only have one range using something like...
WHERE age
BETWEEN
SUBSTRING_INDEX(personsAge,"-",1) + 0 AND
SUBSTRING_INDEX(personsAge,"-",-1) + 0
but how can I accomplish this if I have multiple ranges?
This is an answer extending my comment above.
I'm assuming that you can create a function:
Attention: This is for Sql Anywhere. Please adjust the syntax for MySql (especially the locate-function where the parameters are switched). The code is not production ready and I left out some validity checks. I'm assuming that the values in the column are all well formatted.
Attention 2: This is one of those cases where someone dumps a terrible database design on you and demands that you solve the problem. Please avoid creating the need for solutions like this.
Function:
CREATE FUNCTION "DBA"."is_in_range"(age_range varchar(255), age int)
returns int
begin
declare pos int;
declare pos2 int;
declare strPart varchar(50);
declare strFrom varchar(10);
declare strTo varchar(10);
declare iFrom int;
declare iTo int;
while 1 = 1 loop
set pos = locate(age_range, ',');
if pos = 0 then
-- no comma found in the rest of the column value -> just take the whole string
set strPart = age_range;
else
-- take part of the sting until next comma
set strPart = substr(age_range, 1, pos - 1);
end if;
-- we are parsing the min-max part and do some casting to compare with an int
set pos2 = locate(strPart, '-');
set strFrom = substr(strPart, 1, pos2 - 1);
set strTo = substr(strPart, pos2 + 1);
set iFrom = cast(strFrom as int);
set iTo = cast(strTo as int);
if age between iFrom and iTo then
return 1;
end if;
-- if at end of age_range then quit
if pos = 0 then
return 0;
end if;
-- get the next part of the string after the comma
set age_range = substr(age_range, pos + 1);
set pos = locate(age_range, ',', pos);
end loop;
return 0;
end;
Test data:
create local temporary table #tmpRanges (ident int, age_range varchar(255));
insert into #tmpRanges (ident, age_range) values (1, '27-51,18-28,10-17');
insert into #tmpRanges (ident, age_range) values (2, '37-55,60-70');
insert into #tmpRanges (ident, age_range) values (3, '1-5,11-16,30-32,60-90');
insert into #tmpRanges (ident, age_range) values (4, '1-50');
Call:
select * from #tmpRanges where is_in_range(age_range, 51) = 1;
select * from #tmpRanges where is_in_range(age_range, 10) = 1;
select * from #tmpRanges where is_in_range(age_range, 9) = 1;
etc...

Bitwise operating issue

I'm trying to rebuild a function for generating GUIDs used by the game Arma 3 in a MySQL function. There are some examples in various languages over here: https://gist.github.com/Fank/11127158
Basicially this is what I tried so far:
CREATE DEFINER=`root`#`localhost` FUNCTION `generateGUID`(playerid varchar(17)) RETURNS varchar(32) CHARSET latin1
BEGIN
DECLARE temp bigint;
DECLARE i int;
SET i = 0;
SET temp = 0;
WHILE i < 8 DO
SET temp = temp + CHAR(playerid & 0xFF);
SET playerid = playerid >> 8;
SET i = i + 1;
END WHILE;
RETURN MD5("BE" + temp);
END
What I understood so far orientating at the php example:
First of all we have the steamid given as a varchar.
Afterwards we are iterating 8 times and adding the char of the bitwise addition of the playerid and the value of 0xFF. Then we are performing a 8 bitwise right shift on the playerid.
Afterwards the string "BE" is added the temp result and a md5 is generated of this and returned.
However I'm facing the issue that this function always returns 0. I tried many things so far, like using a blob instead of an int for the temp var.
Edit: After the hint to use CONCAT instead of the + operator the result is still not matching with the guid calculators out there.
The code looks now like this:
CREATE DEFINER=`root`#`localhost` FUNCTION `generateGUID`(playerid varchar(17)) RETURNS varchar(32) CHARSET latin1
BEGIN
DECLARE temp bigint;
DECLARE i int;
SET i = 0;
SET temp = "";
WHILE i < 8 DO
SET temp = CONCAT(temp, CHAR(playerid & 0xFF));
SET playerid = playerid >> 8;
SET i = i + 1;
END WHILE;
RETURN MD5(CONCAT("BE", temp));
END
However, using the playerid 76561197996545192 the function returns 2a0f7ebed67e04afaf7ea032e1ed22e3 instead of cd97cc68c1038b485b081ba2aa3ea6fa which should be the expected output.
This works for me:
DROP FUNCTION IF EXISTS `generateGUID`;
DELIMITER //
CREATE DEFINER=`root`#`localhost` FUNCTION `generateGUID`(`playerid` BIGINT UNSIGNED) RETURNS varchar(32) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE temp text CHARSET ascii;
DECLARE i int;
SET i = 0;
SET temp = "";
WHILE i < 8 DO
SET temp = CONCAT(temp, CHAR(playerid & 0xFF));
SET playerid = playerid >> 8;
SET i = i + 1;
END WHILE;
RETURN MD5(CONCAT("BE", temp));
END//
DELIMITER ;
http://sqlfiddle.com/#!9/6cc709/1

Hi, How to Use for loops in my sql , to print concat() column values of two Tables?

CREATE procedure mydbase.while_example2()
wholeblock:BEGIN
declare str VARCHAR(255) default '';
declare x1 int default 0;
declare x STRING default NULL;
declare y STRING default NULL;
SET x = SELECT*FROM stems;
SET y = SELECT*FROM suffixs;
WHILE x1 <= 5 DO
SET str = CONCAT(str,x,'y');
SET x1 = x1 + 1;
END WHILE;
select str;
END//
mysql> call while_example2();//
You can use group_concat to concat different columns if i get your requirement correctly.
http://www.mysqltutorial.org/mysql-group_concat/

Updating a table row with random number fails

I am using a while loop in a stored procedure to update each row of my table but the update value remains the same for all rows.For instance,in my table the generated number is 2266 for all rows instead of a unique digit.
My stored procedure looks like this
DELIMITER ;;
CREATE PROCEDURE lesrows()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE lesrand INT DEFAULT 0;
SELECT COUNT(*) FROM students INTO n;
select(FLOOR(2000 + RAND() * (3000 - 2000) )) into lesrand;
SET i=0;
WHILE i<n DO
UPDATE students set student_number= lesrand;
SET i = i + 1;
END WHILE;
End;
;;
call lesrows();
Why is it inserting a constant number for all rows instead of a unique number for every row?.
The while statement sets the whole table's student_number's the lesrand on every single iteration. You want a new random number of each iteration (inside the loop) and you want to select students one-by-one using WHERE with your variable i
Something like this:
SET i=0;
WHILE i<n DO
select(FLOOR(2000 + RAND() * (3000 - 2000) )) into lesrand;
UPDATE students set student_number= lesrand WHERE student_ID = i;
SET i = i + 1;
END WHILE;
I haven't tested it, but it looks like lesrand is only assigned once. Have you tried moving select(FLOOR(2000 + RAND() * (3000 - 2000) )) into lesrand; into the while loop like this:
DELIMITER ;;
CREATE PROCEDURE lesrows()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE lesrand INT DEFAULT 0;
SELECT COUNT(*) FROM students INTO n;
SET i=0;
WHILE i<n DO
select(FLOOR(2000 + RAND() * (3000 - 2000) )) into lesrand;
UPDATE students set student_number= lesrand;
SET i = i + 1;
END WHILE;
End;
;;
call lesrows();
And why are you using a stored procedure. Wouldn't UPDATE students SET student_number = FLOOR(2000 + RAND() * (3000 - 2000)) have the same result?

How do you extract a numerical value from a string in a MySQL query?

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