Where is the error in this stored procedure - mysql

DELIMITER //
CREATE PROC InserimentoValori()
BEGIN
DECLARE #caratteri varchar(30);
set #caratteri = 'abcdefghijklmnopqrstuvwxyz',
DECLARE x INT DEFAULT 1;
WHILE x <=100 DO
INSERT INTO Persona(nome,cognome,eta) VALUES((SELECT #caratteri = substring(#caratteri +1),(SELECT #caratteri = sebstring(#caratteri +1),(SELECT floor(rand() * 99) AS randNum));
SET x = x+1;
END WHILE
END //
DELIMITER ;
I want to create a stored procedure that insert random values into the table.
Thanks

There are a couple of errors.
We don't "declare" user defined variables in MySQL. Just SET them.
If you want to DECLARE a variable within a procedure, that needs to be a procedure variable.
A user defined variable has a name that starts with the # character. A procedure variable cannot start with a # character.
So, a line like this is an error:
DECLARE #foo ...
If you want to use a user defined variable, remove that line. If you want to use a procedure variable, remove the # from the beginning of the variable name (and make that same change everywhere you want to reference the procedure variable foo.)
And SEBSTRING is not the name of a MySQL provided function.
Also, a boolean expression in a SELECT list of a query will return 0, 1 or NULL.
For example:
SELECT #caratteri = substring(#caratteri +1)
That expression is comparing the value on the left side of the = with the value on the right, and is going to return 1 if they are equal, or 0 if the aren't, or NULL if either of the values is NULL.
To perform an assignment to a user defined variable in a SELECT statement, use the Pascal-style := operator.
(If you meant to do an assignment, the design makes it look like you are gogin to lop off the first character each time through the loop; that's eventually going to be an empty string, if we loop enough times. You may want to think about leaving the string static. Consider incrementing integer values, and use those as arguments in SUBSTRING function. And you can use the MOD operator to get the integer value to "wrap".)

Related

How to use declared variable to create a json element

I have a stored procedure, in this, I have declared a variable which holds a value from a select function. I need to use this value to create a json element, but it throws an exception
function jsonb_set(jsonb, unknown, character varying, boolean) does not exist
This is the function:
CREATE OR REPLACE FUNCTION test ( ) RETURNS
INTEGER AS $$
DECLARE
intent varchar;
BEGIN
select id into intent from customer;
UPDATE orders
SET data = jsonb_set(
data,
'{Items}', -- the array in which we operate
to_jsonb(
(WITH ar AS(
WITH temp AS(
SELECT data->'Items' AS items -- the array in which we operate
FROM orders
WHERE id = 1 -- the filtered order we are updating
)
SELECT jsonb_set(
jsonb_array_elements(items),
'{Quantity}', -- the new field we are adding
intent, -- this is where i need to replace the variable
true)
FROM temp)
SELECT (array_agg(ar.jsonb_set))
FROM ar)),
false)
WHERE id = 1;
return 0;
EXCEPTION WHEN others THEN
return 1;
END;
$$ LANGUAGE plpgsql;
Copying the snippet where I need to replace the variable:
SELECT jsonb_set(
jsonb_array_elements(items),
'{Quantity}', -- the new field we are adding
intent, -- this is where i need to replace the variable
true)
You have to explicitly cast intent to jsonb using CAST(intent AS jsonb) or intent::jsonb.
The reason it works with a string literal is that such literals are of the (internal) type unknown which can be transformed to most other types, but there is no implicit cast between character varying and jsonb, so you have to use an explicit one.

Stored function in MYSQL

If I have a TABLE named MyTable which has columns say C1(type date) and C2 (type character) I want to create a stored function that takes an input and the input should always belong to C1, and the output of the stored function should be the corresponding element in C2. I have tried to do it using the 'select' statement followed by 'where' clause inside the stored function but was not able to achieve it. Is there any other way to accomplish this task.
DELIMITER $$
CREATE FUNCTION `MyFunction`
(`Date` datetime)
RETURNS char(10)
BEGIN
DECLARE MyVariable char(10)
SELECT MyVariable = `C2`
FROM MyTable
WHERE `Date` = `C1`; RETURN MyVariable;
END$$
DELIMITER ;
But this keeps giving me ERROR CODE: 1064
At first glance, I see a syntax error:
...
BEGIN
DECLARE MyVariable char(10) <-- needs a semicolon here
SELECT MyVariable = `C2`
...
Every statement within the body of your routine must end with a semicolon. See examples of DECLARE in this manual page: https://dev.mysql.com/doc/refman/8.0/en/local-variable-scope.html
It should be like this:
...
BEGIN
DECLARE MyVariable char(10);
SELECT MyVariable = `C2`
...
Re your comment:
Error 1415 means "cannot return a result set". Your stored function is doing a SELECT without putting the result into your declared local variable using an INTO keyword.
You appear to be trying to set the value of MyVariable using = but that's just making a comparison. It doesn't assign anything to MyVariable.
Without using INTO to assign the variable, your SELECT statement is by default returning a result set. This is allowed in a stored procedure, but not in a stored function. A stored function must return a single scalar value, not a result set.
...
BEGIN
DECLARE MyVariable char(10);
SELECT `C2` INTO MyVariable
FROM MyTable
WHERE `Date` = `C1`;
RETURN MyVariable;
END
P.S.: I edited your question to replace the term "user-defined function" with "stored function". These are two different things in MySQL. You are writing a stored function.
In MySQL, they use the term user-defined function (UDF) for a function you implement in C/C++ code and compile into the MySQL server. It's less common for developers to write this type of extension.

How to use split function in mysql or store split value in different column?

String x "3_SJ454FH";
I want to store '3'(Before _ ) in one column and 'SJ454FH'(After _ ) in another column in mysql. I have tried substring_index function in stored procedure but it did not work. So Is there any way to store value like this in stored procedure.?
CREATE DEFINER=`root`#`localhost` PROCEDURE `insert_csv`(_list MEDIUMTEXT)
BEGIN
DECLARE _next TEXT DEFAULT NULL;
DECLARE _nextlen INT DEFAULT NULL;
DECLARE _value TEXT DEFAULT NULL;
iterator:
LOOP
-- exit the loop if the list seems empty or was null;
-- this extra caution is necessary to avoid an endless loop in the proc.
IF LENGTH(TRIM(_list)) = 0 OR _list IS NULL THEN
LEAVE iterator;
END IF;
-- capture the next value from the list
SET _next = SUBSTRING_INDEX(_list,'_',1);
-- save the length of the captured value; we will need to remove this
-- many characters + 1 from the beginning of the string
-- before the next iteration
SET _nextlen = LENGTH(_next);
-- trim the value of leading and trailing spaces, in case of sloppy CSV strings
SET _value = TRIM(_next);
-- insert the extracted value into the target table
INSERT INTO t1 (c1) VALUES (_value);
-- rewrite the original string using the `INSERT()` string function,
-- args are original string, start position, how many characters to remove,
-- and what to "insert" in their place (in this case, we "insert"
-- an empty string, which removes _nextlen + 1 characters)
SET _list = INSERT(_list,1,_nextlen + 1,'');
END LOOP;
END
Output:
id|c1
-----------
1 |3
-----------
2 |SJ454FH
-----------
I have tried this code but it store in next row.
You can use the logic of the following query in combination with INTO to fill two variables with the desired values and then INSERT them in a separate call.
SELECT LEFT("3_SJ454FH", INSTR("3_SJ454FH", "_")-1) AS Prefix,
MID("3_SJ454FH", INSTR("3_SJ454FH", "_")+1, LENGTH("3_SJ454FH")) AS Tail

Mysql: replace string in one table based on information on another table

I have the following problem, this is going to be long, I want to tell exactly all what I know about my problem in my question.
I have a table, field_body_value, with two fields, body_value and body_summary, containing strings of the form "/webfm_send/#" where # is a number.
I have another table called webfm_file where I have two fields with information for the string substitution: the first one is called fid, and it is the number # that I mentioned before, and the second is called fpatch, and gives me a string holding a path (for instance /data/html/files/file1.pdf) which has to substitute /webfm_send/# in the first table. The numbers # go up over the records of webfm_file but there are jumps, that is they increase but there are missing # so the final # is not equal to the number of records in webfm_file
So I thought the strategy was to set up a procedure which loops over the second table, and at each step of the sequence retrieves the pair fid/fpath, searches for "/webfm_send/fid" in the first table, and substitutes this by fpath in the first table.
So this is as far a I could arrive with my coding:
BEGIN
DECLARE v1 INT DEFAULT 0;
SELECT COUNT(*) INTO #numrec FROM `webfm_file`;
WHILE v1 < #numrec DO
SELECT fpath,fid INTO #path,#file FROM `webfm_file` LIMIT v1,1;
SET #webfm = concat('/webfm_send/',#file);
SET #cpath = concat('/',#path);
UPDATE `field_data_body`
SET body_value = replace(body_value, #webfm, #cpath),
body_summary = replace(body_summary, #webfm, #cpath)
WHERE body_value LIKE concat('%',#webfm,'%') OR
body_summary LIKE concat('%',#webfm,'%');
SET v1 = v1 + 1;
END WHILE;
END
Let me explain what I think I'm doing with the code above:
1) I retrieve the number of records in webfm_file for the loop.
2) The first SELECT gets a pair in fpath/fid from webfm_file, with LIMIT v1,1 I just check one record at a time, I checked an it works, the while loops over each record of webfm_file and the records are retrieved correctly.
3) The two next "set" fix the pair of strings #file/#path to create #webfm whith is the way its written in body_value at field_body_value, and to put a slash in front of #cpath which is the way I need this string to finally appear.
4) Then comes the UPDATE which will actually substitute the string if it finds it in either body_value or body_summary of field_body_data.
Expected: each instance of /webfm_send/# is substituted by the corresponding fpath pair of # (fid) in webfm_file
What I actually get: All appearances of /webfm_send/# no matter the value of # are substituted by the value of fpath in record 1 of webfm_file.
Things I have tried:
1) Take out the "WHERE" clause in the UPDATE sentence, which I believe is not strictly necessary since the replace function already takes care of finding a match but could speed up things. Same result
2) Resctrict the loop to loop just over a single record of webfm_file. Here it works in substituting the corresponding single retrieved pair fid/fpath, in the two instances of body_value and body_summary in field_body_data where fid=# appears in the string webfm_send/#
Thanks for following my explanation until here and thanks in advance for any hint.
You could use a cursor to iterate over the replacement strings. (There are faster ways using group_concat and it would be easier to do this in a general-purpose language rather than in a stored procedure). The general cursor approach would be:
drop procedure if exists proc;
delimiter //
create procedure proc()
begin
declare done boolean default 0;
declare path varchar(255);
declare id int;
declare cur cursor for select fpath, fid from webfm_file order by fid desc;
declare continue handler for sqlstate '02000' set done = 1;
open cur;
block: loop
fetch cur into path, id;
if done then
leave block;
end if;
set #from = concat('/webfm_send/', id);
update field_data_body set
body_value = replace(body_value, #from, path),
body_summary = replace(body_summary, #from, path);
end loop;
close cur;
end//
delimiter ;
call proc();

SSRS multi-value parameter using a stored procedure

I am working on a SSRS report that uses a stored procedure containing a few parameters. I am having problems with two of the parameters because I want to have the option of selecting more than one item.
Here's a condensed version of what I have:
CREATE PROCEDURE [dbo].[uspMyStoredProcedure]
(#ReportProductSalesGroupID AS VARCHAR(MAX)
,#ReportProductFamilyID AS VARCHAR(MAX)
,#ReportStartDate AS DATETIME
,#ReportEndDate AS DATETIME)
--THE REST OF MY QUERY HERE WHICH PULLS ALL OF THE NEEDED COLUMNS
WHERE DateInvoicedID BETWEEN #ReportStartDate AND #ReportEndDate
AND ProductSalesGroupID IN (#ReportProductSalesGroupID)
AND ProductFamilyID IN (#ReportProductFamilyID)
When I try to just run the stored procedure I only return values if I enter only 1 value for #ReportProductSalesGroupID and 1 value #ReportProductFamilyID. If I try to enter two SalesGroupID and/or 2 ProductFamilyID it doesn't error, but I return nothing.
-- Returns data
EXEC uspMyStoredProcedure 'G23', 'NOF', '7/1/2009', '7/31/2009'
-- Doesn't return data
EXEC uspMyStoredProcedure 'G23,G22', 'NOF,ALT', '7/1/2009', '7/31/2009'
In SSRS I get an error that says:
Incorrect syntax near ','
It appears that the , separator is being included in the string instead of a delimiter
You need three things:
In the SSRS dataset properties, pass the multi-value param to the stored procedure as a comma-delimited string
=Join(Parameters!TerritoryMulti.Value, ",")
In Sql Server, you need a table-value function that can split a comma-delimited string back out into a mini table (eg see here). edit: Since SQL Server 2016 you can use the built-in function STRING_SPLIT for this
In the stored procedure, have a where clause something like this:
WHERE sometable.TerritoryID in (select Item from dbo.ufnSplit(#TerritoryMulti,','))
... where ufnSplit is your splitting function from step 2.
(Full steps and code in my blog post 'SSRS multi-value parameters with less fail'):
Let us assume that you have a multi value list #param1
Create another Internal Parameter on your SSRS report called #param2 and set the default value to:
=Join(Parameters!param1.value, 'XXX')
XXX can be any delimiter that you want, EXCEPT a comma (see below)
Then, you can pass #param2 to your query or stored procedure.
If you try to do it any other way, it will cause any string function that uses commas to separate arguments, to fail. (e.g. CHARINDEX, REPLACE).
For example Replace(#param2, ',', 'replacement') will not work. You will end up with errors like "Replace function requires 3 arguments".
Finally I was able to get a simple solution for this problem. Below I have provided all (3) steps that I followed.
I hope you guys will like it :)
Step 1 - I have created a Global Temp Table with one column.
CREATE GLOBAL TEMPORARY TABLE TEMP_PARAM_TABLE(
COL_NAME VARCHAR2(255 BYTE)
) ON COMMIT PRESERVE ROWS NOCACHE;
Step 2 - In the split Procedure, I didn't use any array or datatable, I have directly loaded the split values into my global temp table.
CREATE OR REPLACE PROCEDURE split_param(p_string IN VARCHAR2 ,p_separator IN VARCHAR2
)
IS
v_string VARCHAR2(4000);
v_initial_pos NUMBER(9) := 1;
v_position NUMBER(9) := 1;
BEGIN
v_string := p_string || p_separator;
delete from temp_param_policy;
LOOP
v_position :=
INSTR(v_string, p_separator, v_initial_pos, 1);
EXIT WHEN(NVL(v_position, 0) = 0);
INSERT INTO temp_param_table
VALUES (SUBSTR(v_string, v_initial_pos
, v_position - v_initial_pos));
v_initial_pos := v_position + 1;
END LOOP;
commit;
END split_param;
/
Step 3 - In the SSRS dataset parameters, I have used
=Join(Parameters!A_COUNTRY.Value, ",")
Step 4: In the start of your stored procedure executes the Procedure
Exec split_param(A_Country, ‘,’);
Step 5: In your stored procedure sql use the condition like below.
Where country_name in (select * from TEMP_PARAM_TABLE)
When SSRS passes the parameter it is in the form: Param1,Param2,Param3.
In the procedure, you just need to put identifiers around each parameter. And also identifiers around the value that is returned by the dataset. In my case, I used semicolons.
CREATE OR REPLACE PROCEDURE user.parameter_name (
i_multivalue_parameter
)
AS
l_multivalue_parameter varchar2(25555) := ';' || replace(i_multivalue_parameter,',',';') || ';';
BEGIN
select something
from dual
where (
instr(l_multivalue_parameter, ';' || database_value_that_is_singular || ';') > 0
)
END;
i_multivalue_parameter is passed in via SSRS.
l_multivalue_parameter reads the parameter passed in via SSRS and puts identifiers around each value.
database_value_that_is_singular is the value returned for each record.
So if 'Type1,Type2,Type3'is passed in via SSRS:
i_multivalue_parameter is: Type1,Type2,Type3
l_multivalue_parameter is: ;Type1;Type2;Type3;
database_value_that_is_singular is: ;Type1; or ;Type2; or ;Type3;
Instr will return a value over 0 if the parameter matches.
This works even if each parameters are similar. EG: "Type A" and "Type AA". That is "Type A" will not match "Type AA".
I found a simple way for my solution. Define the parameter value in the report as an expression like this
="'" + Join(Parameters!parm.Value,"','") + "'"
(in case you can't read it the first and last literals are double quote, single quote, double quote. The join literal is double quote, single quote, comma, single quote, double quote)
Then in the stored procedure you can use dynamic sql to create your statement. I did this to create a temp table of values to join to in a later query, like this:
CREATE #nametable (name nvarchar(64))
SET #sql = N'SELECT Name from realtable where name in (' + #namelist + ')'
INSERT INTO #nametable exec sp_executesql #sql
#namelist would be the name of the stored procedure parameter.