PHP foreach in SQL - mysql

I need to generate a database with uniqe codes a-z, A-Z and 0-9 with 6 digits or more(62^6=56800235584 uniqe codes)
Until now i have this code for sql:
CREATE FUNCTION cod12() RETURNS VARCHAR(10)
BEGIN
DECLARE chars VARCHAR(62);
DECLARE result VARCHAR(6);
DECLARE i INT;
SET chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvxyz';
SET result = '';
SET i = 0;
label: LOOP
SET result = CONCAT(result, SUBSTRING(chars, FLOOR(RAND()*62) + 1, 1));
SET i = i + 1;
IF i = 10 THEN
LEAVE label;
END IF;
END LOOP label;
RETURN result;
when i call function is generate a random code, from my tests i can instert 10.000 codes per seconds.
PHP version, wich is perfect solution but can generate only 300 codes per seconds, and if fails will start from begining, is a simple foreach code:
$char=array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q');
foreach ($char as $c) {
foreach ($char as $c2) {
foreach ($char as $c3) {
foreach ($char as $c4) {
echo $c['c'].$c2['c'].$c3['c'].$c4['c'].'<br>';
}}}}
Question:Loop in sql don't work, it generate only one code, how i can generate codes in order way(00,01,02...0t,0u,21,22,23,2b,2c...gj,gk,gl), not random, in sql command not php.
Thanks in advance!

Related

SQL : Multiple String Replace (Add a space in front of capital letter(s) starting with 2nd capital letter)

I've updated the Name_Table.column_2 data with wrong strings. Instead of 'John Smith',I've updated with 'JohnSmith'.
Now i would like to replace multiple strings,For Example : 'JohnSmith' as 'John Smith' , 'JohnDoe' as 'John Doe' etc.
I'm not familiar with SQL, can some one help me on how to replace multiple strings all at once.
#Name_Table
Column_1 Column_2
1 JohnSmith
2 JohnSmith
3 JohnDoe
4 JohnSmith
5 WayneRooney
6 JohnDoe
7 WayneRooney
8 JohnSmith
9 WayneRooney
10 JohnDoe
I dont know single query for this situation, but try below method to solve your problem. I'm sure it works fine for you.
$sel = mysql_query('SELECT * FROM Name_Table;');
while($row = mysqli_fetch_array($sel)){
$string = $row['Column_2'];
$count = strlen($string);
$strings = array();
$i = 0;
$ii = 0;
while($i < $count)
{
$char = $string{$i};
if(ereg("[A-Z]", $char, $val)){
$ii++;
$s = '';
$s .= $char;
} else {
$s .= $char;
}
$strings[$ii] = $s;
$i++;
}
$name_with_space = implode(' ',$strings);
mysql_query('UPDATE Name_Table SET Column_2="'.$name_with_space.'" WHERE Column_1='.$row['Column_1']);
}
if your names are always gonna be in the format of FirstnameLastname
you can do a custom function like this
CREATE FUNCTION breakup_name (fullname varchar(50))
RETURNS VARCHAR(50)
BEGIN
SET #fullname = fullname,#newname='',#letter='',#capcount=0,#space='';
WHILE LENGTH(#fullname)>0 DO
SET #letter = LEFT(#fullname,1);
SET #space = '';
IF #letter RLIKE BINARY '[A-Z]' THEN
SET #capcount = #capcount+1;
IF #capcount >= 2 THEN SET #space = ' '; END IF;
END IF;
SET #newname = CONCAT(#newname,#space,#letter);
SET #fullname = RIGHT(#fullname,LENGTH(#fullname)-1);
END WHILE;
RETURN #newname;
END/
then use an UPDATE like this.
UPDATE table1 SET column_2 = breakup_name(column_2);
sqlfiddle
P.S. In the sqlfiddle I used / as delimiter, you'll have to change that according to your delimiter.
The above function will add a space in front of a capital letters (starting with the 2nd capital letter), so for example if you had TommyJaeSmith, it'll return Tommy Jae Smith.
The logic of this function is very simple, it loops through and looks at each letter. If the letter is a capital letter it increments a capcount, if the capcount is greater than or equal to 2 (meaning 2nd capital letter or after) it adds a space... to be concatenated in front of the letter.

Iterate through JSON in MySQL query

I'm looking for something like forEach for a JSON array in MySQL.
I manager IDs in MySQL JSON data type like this: [1, 2, 3, 4, 5], and I want to perform an action for each item in the list.
A naive solution is to do a WHILE loop with a counter that starts at 0 and ends when VAR_MANAGER_ID is null. Here is a contrived example of how the inside of the WHILE loop would look:
SET VAR_PATH = CONCAT('$[', COUNTER, ']');
SET VAR_MANAGER_ID = JSON_PARSE(VAR_MANAGER_IDS, PATH);
# See if we've reached the end of the list
IF VAR_MANAGER_ID IS NULL
THEN
BREAK
END;
INSERT INTO LU_MANAGER (MANAGER_ID) VALUES (VAR_MANAGER_ID);
But there has to be a better way! How can I do something like:
FOREACH JSON_PARSE(VAR_MANAGER_IDS, '$[*]') AS VAR_MANAGER_ID
INSERT INTO LU_MANAGER (MANAGER_ID) VALUES (VAR_MANAGER_ID);
complete newbie here but I found a way to iterate a JSON array using REPEAT-UNTIL-END REPEAT
REPEAT
SET txt = JSON_EXTRACT(myjson, CONCAT("$[", indx, "]"));
# use txt
SET indx = indx + 1;
UNTIL indx = JSON_LENGTH(myjson)
END REPEAT;

How to get a value from func/sp to a case statement using dynamic SQL?

hoping someone might be able to help me with a bit of an issue. Essentially i'm trying to get a rough size of all of the fields in my database as i'd like to do some math on it to guesstimate what the size will be with a compression technique applied to it.
I can do this for most fields by looking at the datatype and using the number of rows to get the number of bytes it's taking up. However on something like a varchar(max) field this is not as easy and so i decided to approach this by getting the average length within the column and multiplying by number of rows. But i've hit a snag which i'll describe below.
I have the following stored proc (i tried a function too but you can't call dynamic SQL from a function).
CREATE PROCEDURE dbo.getFieldSize(#column varchar(255), #table varchar(255), #ret decimal(15,7) OUTPUT)
AS
BEGIN
DECLARE #lengthSQL varchar(50)
/SET #lengthSQL = 'SELECT #ret = AVG(DATALENGTH(' + #column + ')) FROM [' + #table +']'/
SET #lengthSQL = 'SELECT #ret = AVG(DATALENGTH(' + #column + ')) FROM ' + #table
exec sp_executesql #lengthSQL
RETURN #ret
END
GO
And then i call it using...
SELECT b.TABLE_SCHEMA as 'Schema',
CASE WHEN DATA_TYPE IN ('nvarchar') AND CHARACTER_MAXIMUM_LENGTH <> -1 AND c.distinctItems <> 0 AND c.totalCount <> 0 THEN exec('select max(len(' + b.TABLE_CATALOG + ' + '.' + ' + b.COLUMN_NAME + '))')
FROM ....
The above is basically just checking to make sure it is a varchar(max) field and contains some values within the column. I then try and execute the SP and pass the column name and table name for which i need the avg length but i get the following error.
Msg 156, Level 15, State 1, Line 57
Incorrect syntax near the keyword 'exec'.
I learned you cannot call a dynamic SQL from a function and you cannot call a SP from a CASE statement. So at this point it seems like it's a catch 22 and i cannot do what i need using SQL. Can anyone think of any workarounds or i'm I out of luck on this?
Actually, you can do Dynamic SQL in a scalar UDF, it just needs to be a SQLCLR UDF ;-). But this is fairly simple to do using the in-process / internal connection (i.e. SqlConnection("Context Connection = true;");). Meaning, the assembly can be set to SAFE.
Also, object / column / index names are all NVARCHAR. And objects (if not also the others) are declared as sysname which is an alias for NVARCHAR(128). Just FYI.
So, something like the following (which I have tested and it does work):
[Microsoft.SqlServer.Server.SqlFunction(Name = "GetAvgBytes",
IsDeterministic = false, IsPrecise = true, DataAccess = DataAccessKind.Read)]
public static SqlInt32 GetAvgBytes([SqlFacet(MaxSize = 128)] SqlString TableName,
[SqlFacet(MaxSize = 128)] SqlString ColumnName)
{
int _AvgBytes = -1;
SqlConnection _Connection = new SqlConnection("Context Connection = true;");
SqlCommand _Command = _Connection.CreateCommand();
_Command.CommandType = CommandType.Text;
_Command.CommandText = "SELECT #AvgBytes = AVG(DATALENGTH(" + ColumnName.Value
+ ")) FROM " + TableName.Value + " WITH (NOLOCK);";
SqlParameter _Param = new SqlParameter("#AvgBytes", DbType.Int32);
_Param.Direction = ParameterDirection.Output;
_Command.Parameters.Add(_Param);
try
{
_Connection.Open();
_Command.ExecuteNonQuery();
_AvgBytes = (int)_Param.Value;
}
finally
{
_Connection.Close();
}
return _AvgBytes;
}

Postgresql: dump of input key/value pairs into a string variable in PL/pgSQL

I need to find a way to dump key/value pairs of PL/pgSQL function input parameters:
CREATE OR REPLACE FUNCTION _test(A text, B text)
...
raise info 'Params dump: %', _x;
...
when executed:
select _text('HELLO', 'WORLD');
the function raises info as follows:
'A = HELLO, B = WORLD'
Is there a way to dump input parameter key/value pairs into a variable?
It's possible if you can make the function VARIADIC with uniform argument types, and can print the array. You don't get argument names, since they don't have names, but you do get argument positions.
Otherwise no, it is not possible in PL/PgSQL, though it should be in other PLs like PL/Perl, PL/Python, etc.
It'd be quite nice to be able to get a RECORD with all the function arguments in it, so you could print it, feed it to the hstore extension, etc, but this isn't currently possible.
There is an awkward way of dumping input parameters :
create or replace function _tester(
_txt text,
_int int
) returns void
language 'plpgsql' as
$$
declare
_out text = '';
_rec record;
_func_name text = '_tester';
begin
for _rec in SELECT parameters.ordinal_position as _pos, parameters.parameter_name as _nm
FROM information_schema.routines
JOIN information_schema.parameters
ON routines.specific_name=parameters.specific_name
WHERE routines.routine_name = _func_name
ORDER BY parameters.ordinal_position
loop
if _rec._pos = 1 then
_out = _out || _rec._nm || ' = ' || $1::text || chr(10);
elsif _rec._pos = 2 then
_out = _out || _rec._nm || ' = ' || $2::text || chr(10);
end if;
end loop;
raise notice '%', _out;
end;
$$;
select _tester('A','1');
NOTICE: _txt = A
_int = 1
Notice that must add as many if/elsif as there are input parameters. Not sure if that part can be more concise.

How to find the first number in a text field using a MySQL query?

I like to return only the first number of a text stored in a column of a database table.
User have put in page ranges into a field like 'p.2-5' or 'page 2 to 5' or '2 - 5'.
I am interested in the '2' here.
I tried to
SELECT SUBSTR(the_field, LOCATE('2', the_field, 1)) AS 'the_number'
FROM the_table
and it works. But how to get ANY number?
I tried
SELECT SUBSTR(the_field, LOCATE(REGEXP '[0-9], the_field, 1)) AS 'the_number'
FROM the_table
but this time I get an error.
Any ideas?
Just use REGEXP_SUBSTR():
SELECT REGEXP_SUBSTR(`the_field`,'^[0-9]+') AS `the_number` FROM `the_table`;
Notes:
I'm using MySQL Server v8.0.
This pattern assumes that the_field is trimmed. Otherwise, use TRIM() first.
REGEXP is not a function in MySQL, but something of an operator. Returns 1 if field matches the regular expression, or 0 if it does not. You cannot use it to figure out a position in a string.
Usage:
mysql> SELECT 'Monty!' REGEXP '.*';
-> 1
As for answer to the question: I don't think there is a simple way to do that using MySQL only. You would be better off processing that field in the code, or extract values before inserting.
For the specific case in the question. Where the String is {number}{string}{number}
there is a simple solution to get the first number. In our case we had numbers like 1/2,3
4-10
1,2
and we were looking for the first number in each row.
It turned out that for this case one can use convert function to convert it into number. MySQL will return the first number
select convert(the_field ,SIGNED) as the_first_number from the_table
or more hard core will be
SELECT
the_field,
#num := CONVERT(the_field, SIGNED) AS cast_num,
SUBSTRING(the_field, 1, LOCATE(#num, the_field) + LENGTH(#num) - 1) AS num_part,
SUBSTRING(the_field, LOCATE(#num, the_field) + LENGTH(#num)) AS txt_part
FROM the_table;
This was original post at source by Eamon Daly
What does it do?
#num := CONVERT(the_field, SIGNED) AS cast_num # try to convert it into a number
SUBSTRING(the_field, 1, LOCATE(#num, the_field) + LENGTH(#num) - 1) # gets the number by using the length and the location of #num in field
SUBSTRING(the_field, LOCATE(#num, the_field) + LENGTH(#num)) # finds the rest of the string after the number.
Some thoughts for future use
Its worth keeping another column which will hold the first number after you parsed it before insert it to the database. Actually this is what we are doing these days.
Edit
Just saw that you have text like p.2-5 and etc.. which means the above cannot work as if the string does not start with a number convert return zero
There's no built-in way that I know of, but here's a Mysql function you can define, this will do it (I didn't code for minus-signs or non-integers, but those could of course be added).
Once created, you can use it like any other function:
SELECT firstNumber(the_field) from the_table;
Here's the code:
DELIMITER //
CREATE FUNCTION firstNumber(s TEXT)
RETURNS INTEGER
COMMENT 'Returns the first integer found in a string'
DETERMINISTIC
BEGIN
DECLARE token TEXT DEFAULT '';
DECLARE len INTEGER DEFAULT 0;
DECLARE ind INTEGER DEFAULT 0;
DECLARE thisChar CHAR(1) DEFAULT ' ';
SET len = CHAR_LENGTH(s);
SET ind = 1;
WHILE ind <= len DO
SET thisChar = SUBSTRING(s, ind, 1);
IF (ORD(thisChar) >= 48 AND ORD(thisChar) <= 57) THEN
SET token = CONCAT(token, thisChar);
ELSEIF token <> '' THEN
SET ind = len + 1;
END IF;
SET ind = ind + 1;
END WHILE;
IF token = '' THEN
RETURN 0;
END IF;
RETURN token;
END //
DELIMITER ;