How to use nested loops and variables in MySQL statement - mysql

I'm trying to write a sql statement that will insert data given a few setup variables. I don't want to create a stored procedure, as it's not something I'll be dealing with anywhere except as an administrator and I don't want the stored procedure hanging around. I just want a statement that works. Here's what I have so far:
SET #app = 7;
SET #time = UNIX_TIMESTAMP(NOW());
SET #maxValue = 100;
SET #decrement = 10;
SET #category = 1;
SET #minSubcategory = 0;
SET #maxSubcategory = 19;
SET #subcategory = #minSubcategory;
subcat_loop: LOOP
SET #idx = 0;
insert_loop: LOOP
SET #id = CONCAT('TempId', #idx+1);
SELECT #name:=username FROM user WHERE id = #id;
SET #value = #maxValue - #decrement * #idx;
INSERT INTO data (userId, username, app, category, subcategory, value, date) VALUES
(#id, #name, #app, #category, #subcategory, #value, #time);
SET #idx = #idx+ 1;
IF #idx > 10 THEN
LEAVE insert_loop;
END IF;
END LOOP insert_loop;
SET #subcategory = #subcategory + 1;
IF #subcategory > #maxSubcategory THEN
LEAVE subcat_loop;
END IF;
END LOOP subcat_loop;
But it doesn't like the SET #idx = 0 inside the loop for some reason. What am I doing wrong?
Note that this is probably the first time I've tried doing anything this complicated with MySQL, and my little knowledge is probably more dangerous than being completely oblivious, so let me know if I'm going about this the wrong way completely (although I really, really don't want a stored procedure for this).

Unfortunately you can't use LOOP outside of a stored program: stored procedures, stored functions, and triggers.
You do have some options:
You can create a stored procedure and restrict the privileges so other users can't execute it.
Another option is to create a stored procedure temporarily in your script, run it, then drop it at the end of the script.
Otherwise, if you still don't want to create a stored procedure, your best bet is to write a small script in shell, python, etc to do your looping.
Good luck!

Related

How to omit html tags in a mysql table attribute while doing a select

I have a table where each row consist of an attribute which consist of html data with like this.
<div className="single_line"><p>New note example</p></div>
I need to omit the html tags and extract only the data inside the tags using sql query. Any idea on how to achieve this?. I tried out different regex but they didnt work.
There are 2 solutions based on mysql version.
If you are using MySQL 8.0 then you can use REGEXP_REPLACE() directly inside the select statement.
SELECT REGEXP_REPLACE('<div><p>New note example</p></div>', '(<[^>]*>)|( )', '');
If you are using MySQL 5.7 then you have to create a user define function in database to strip html tags.
DROP FUNCTION IF EXISTS fn_strip_html_tags;
CREATE FUNCTION fn_strip_html_tags( html_text TEXT ) RETURNS TEXT
BEGIN
DECLARE start,end INT DEFAULT 1;
DECLARE text_without_nbsp TEXT;
LOOP
SET start = LOCATE("<", html_text, start);
IF (!start) THEN RETURN html_text; END IF;
SET end = LOCATE(">", html_text, start);
IF (!end) THEN SET end = start; END IF;
SET text_without_nbsp = REPLACE(html_text, " ", " ");
SET html_text = INSERT(text_without_nbsp, start, end - start + 1, "");
END LOOP;
END
For example
SELECT fn_strip_html_tags('<div><p>New note example</p></div>');

basic cursor mysql with loop

I've been trying to create a cursor but for some reason i keep getting an error saying theres something wrong with the syntax.
what im asking is for a basic cursor that i can use with a loop so that i can call a function for each row. literally a mysql equivelent of php foreach loop
function functionName ($commentId){
$i = 0;
foreach (commentId as value) {
//inside here we would do whatever function we would like but for this example i just want a basic increment to keep things simple and easy to understand
$i++;
}
echo $i;}
functionName(76);
i understand php is a little bit different as i would first need to select the rows using a mysql query and have the $commentId variable equal to the but i was just using this as an example to make this question as clear as i can make it.
here is my example that keeps failing
DELIMITER $$
create function functionName(commentOn int(11)) returns int
BEGIN
DECLARE done INT DEFAULT FALSE;
declare var int;
declare anotherVar int;
set var = 0;
set anotherVar = 0;
DECLARE cur1 CURSOR FOR SELECT id FROM comments where `commentOnComment` = commentOn;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO anotherVar;
set var = var + 1;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE cur1;
return var;
END; $$
DELIMITER ;
select functionName(76);
to explain what this example is in a little more detail, there is a table full of comments, the comment id and the id of the comment the user commented on (commentOnComment). and in the end i want to loop through all of the comments and select all where the commentOnComment equals the id of the comment the user commented on so that i can see how much replies each comment has i will change the increment to a select count(*) from comments where commentOnComment = commentOn; the commentOn parameter will be the id of the comment. But for the answer shown for this question i just want an increment to keep it simple and easy for myself and other people to understand.
Thanks to P.Salmon i was able to get a basic cursor with loop working, it was just the order of my code that was the problem. Im very greatfull as a little mistake like this would have taken me forever to figure out especially with the fact that i had never done a working cursor before.
here is the code for a basic cursor that does a function for each row in a table, in this example it increments the value of a variable by 1.
DELIMITER $$
create function functionName(commentOn int(11)) returns int
BEGIN
DECLARE done INT DEFAULT FALSE;
declare var int;
declare anotherVar int;
DECLARE cur1 CURSOR FOR SELECT id FROM comments where `commentOnComment` = commentOn;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
set var = 0;
set anotherVar = 0;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO anotherVar;
IF done THEN
LEAVE read_loop;
END IF;
/*comment here is the area where you can add whatever insert, update, select or any
type of function you would like for each row. This is literally mysql's version of
php's foreach loop*/
set var = var + 1;
END LOOP;
CLOSE cur1;
return var;
END; $$
DELIMITER ;
as you can see its very similar to the question i just needed to change the order of my code. a simple select functionName(id); will give you the output, the id can be any number, in this example it would be the id of the row.
I am also curious as to why many people viewed this question but didnt answer, consequentially causing me to have to answer myself? if P.Salmon didnt comment and tell me the problem i would still be stuck. i tried to be as clear as i could, was it difficult to understand? was the question too hard for most people? feedback would be highly appreciated as it will help me in my future questions.

MySql Procedure GOTO Statement

I want to use goto statement in Mysql stored procedure in such manner that I execute my cursor whenever condition become true once again.
This is a sample code only, I just looking for goto statement
BLOCK_BACKLOG:begin
declare backlgId, oldCOR, oldATR,oldCourse,oldATR,isFormFilled,nextParentId int;
declare backlogNoMoreRow boolean default false;
Label : TestLineStart
declare backlogCur cursor for select bcklg.id,cor.id,atr.id,cr.id,atr.obtainedMarks,atr.isFormFilled,atr.parentRegistration_id
from train bcklg,bus cor,fliet atr,fair co,distance cr
where bcklg.courseofferingregistration_id=cor.id and cor.academictermregistration_id=atr.id and cor.courseoffering_id=co.id and co.course_id=cr.id
and bcklg.isDeleted is false and atr.id=parentId;
declare continue handler for not found set backlogNoMoreRow=true;
open backlogCur;
LOOP_BACKLOG: loop
fetch backlogCur into backlgId, oldCOR, oldATR,oldCourse,oldATRMarks,isFormFilled,nextParentId;
if backlogNoMoreRow then
close backlogCur;
leave LOOP_BACKLOG;
end if;
if isFormFilled==0 then
parentId=nextParentId;
GOTO TestLineStart;
end if;
You can use a LOOP on the outer block based on the value for variable isFormFilled.
Following change may help you.
-- Label : TestLineStart
TestLineStart: LOOP
-- other part of your SP as it is
and make following change:
if isFormFilled==0 then
parentId=nextParentId;
-- GOTO TestLineStart;
else
leave TestLineStart;
end if;
You have to close all loop statements properly.

Calling TEdit objects based on DB query

I have a form with 7 TEdit having name EditPhone1, EditPhone2 and so on.
In the same form I query a DB to get data to fill those TEdits. Of course I cannot know in advance how many results the query will return.
How can I call the various TEdit objects when looping on the rowcount of the query?
Use FindComponent to "convert" a component name to the component itself:
var
Edit: TEdit;
I: Integer;
begin
DataSet.First;
I := 1;
while not DataSet.Eof do
begin
Edit := TEdit(FindComponent(Format('EditPhone%d', [I])));
if Edit <> nil then
Edit.Text := DataSet.FieldValues['PhoneNo'];
DataSet.Next;
Inc(I);
end;
Now, this requires to hard-code the EditPhone%d string into the source which results in all kinds of maintainability issues. For example: consider renaming the edits.
Alternative 1:
To not rely on the component names, you could instead make use of TLama's idea and add all the edits to a list:
uses
... , Generics.Collections;
type
TForm1 = class(TForm)
EditPhone1: TEdit;
...
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FEdits: TList<TEdit>;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FEdits := TList<TEdit>.Create;
FEdits.AddRange([EditPhone1, EditPhone2, EditPhone3, EditPhone4, EditPhone5,
EditPhone6, EditPhone7]);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FEdits.Free;
end;
procedure TForm1.ADOQuery1AfterOpen(DataSet: TDataSet);
var
I: Integer;
begin
DataSet.First;
I := 0;
while (not DataSet.Eof) and (I < FEdits.Count) do
begin
FEdits[I].Text := DataSet.FieldValues['PhoneNo'];
DataSet.Next;
Inc(I);
end;
end;
This still requires some maintenance in case of adding edits in future.
Alternative 2:
You could also loop over all edits in the form to find the ones tagged to be added to the list, instead of adding them each explicitly:
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
begin
FEdits := TList<TEdit>.Create;
for I := 0 to ComponentCount - 1 do
if (Components[I] is TEdit) and (TEdit(Components[I]).Tag = 1) then
FEdits.Add(TEdit(Components[I]));
end;
But keeping those tags up to date is another burden.
Alternative 3:
I suggest you use a TDBGrid which is a data-component. Opening the linked dataset will automatically add all phone numbers to the grid. With some settings, the grid may kind of look like a couple of edits below each other.
You can, for example, use Tag property, to find needed component. Set all you TEdit's tag from 1 to 7 (or more), and find component by:
Var I: Integer;
MyEdit : TEdit;
For I = 0 To Self.ComponentCount - 1 Do
if (Self.Components[I] IS TEdit) AND (Self.Components[I] AS TEdit).Tag = YourTag
MyEdit = (Self.Components[I] AS TEdit);
You can also dynamically create so many TEdits, you need, and assign Tag property on creation, and find it this code later in runtime.
I'd suggest using DBCtrlGrid. You place your controls for one row on it, and it repeats the controls for as many rows as your data set has.
Get query result (usually using .RowCount property of TDataset return)
After getting the number of row, do iteration to make TEdit and set the text property
Here is sample of code:
...
For i:=0 to RowCount do
Begin
A:=TEdit.Create(self);
A.Parent:=AForm;
A.Top:=i*14;
A.Text:=ADataset.Field(i).AsString;
End;
...

How to use goto label in MySQL stored function

I would like to use goto in MySQL stored function.
How can I use?
Sample code is:
if (action = 'D') then
if (rowcount > 0) then
DELETE FROM datatable WHERE id = 2;
else
SET p=CONCAT('Can not delete',#b);
goto ret_label;
end if;
end if;
Label: ret_label;
return 0;
There are GOTO cases which can't be implemented in MySQL, like jumping backwards in code (and a good thing, too).
But for something like your example where you want to jump out of everything to a final series of statements, you can create a BEGIN / END block surrounding the code to jump out of:
aBlock:BEGIN
if (action = 'D') then
if (rowcount > 0) then
DELETE FROM datatable WHERE id = 2;
else
SET p=CONCAT('Can not delete',#b);
LEAVE aBlock;
end if;
end if;
END aBlock;
return 0;
Since your code is just some nested IFs, the construct is unnecessary in the given code. But it makes more sense for LOOP/WHILE/REPEAT to avoid multiple RETURN statements from inside a loop and to consolidate final processing (a little like TRY / FINALLY).
There is no GOTO in MySQL Stored Procs. You can refer to this post:
MySQL :: Re: Goto Statement