SQL how to extract a value from a variable - mysql

I'm looking for a way to extract the value from a SQL variable and save this value in a new variable. For example, if I have the variable #myVar, which is storing the value 25, and I want to save this value to a new variable #myVar1 before updating the value stored in #myVarto 30. I've found that simply setting #myVar1:= #myVar does not work as once I change the value stored in #myVarto 30 the #myVar1 variable will automatically update itself to 30 as well.
I am using these variables in a SELECT statement in order to keep track of and compare values in a previous row to values in the current row.
The relevant code snippet is:
SELECT
CASE
When (#r is not null) then #myVar1:= #myVar
END,
#myVar:= #myVar + 1
As you can see, #myVar is set to a new value every row, while #myVar1 should only be set to the current value of #myVar if the condition is met, and remain storing that value until the condition is met again, regardless of any changes to the value #myVar is storing. So if #myVar = 7 and #r is not null then #myVar1 = 7, but when #myVar will then be set to 8, #myVar1 will also be set to 8 instead of remaining at 7 until the condition is met again.

Maybe you should do something like this:
#x_old := x;
#x := case when #r is not null then #y else #x end;
(If this is in a procedure, you can leave the ; right where they are; if this is inside a query, replace them with ,)
This way you will preserve the value of #x if the condition is not fulfilled

Related

Trying to execute an update query where rows are variables from TEdit

I have a table which has values that need to be updated. There are quite a few typos as different people have added items in the past, so I have to use 'like' despite everything.
I created a Form with all the necessary fields that need updating. I used simple TEdit and a TLabel. The plan is that one picks a type of item from an edit box and also use a TListBox.
Then one adds the information to be updated into each TEdit.
I create an update procedure as follows:
MyQuery1.SQL.Text := ' UPDATE TheTable SET iName = edtName.text WHERE iType like "%edtType.text%" ';
MyQuery1.SQL.Text := ' UPDATE TheTable SET iPart = edtPart.text WHERE iType like "%edtType.text%" ';
...
MyQuery.ExecSQL;
My problem is passing the edtVariables in the correct format. How does one add the "% before and %" after the edtVariable?
I tried '"%'+edtPart.text+'%"' and such like, but I've never had to do this at runtime before.
Using FireDAC in a parametrized query, you use ':' in front of a parameter name:
MyQuery1.SQL.Text := 'UPDATE TheTable SET iName = :NameText WHERE iType like :TheValue';
Then you set the parameter value before executing the request:
MyQuery1.ParamByName('TheValue').AsString := EdtType.Text;
MyQuery1.ParamByName('NameText').AsString := EdtName.Text;
MyQuery1.ExecSQL;

canon set a variable using the if statement

I have a problem with setting the #temp variable.
The if statement below fails. I have tried various combinations with quotes, using the SELECT command, but I can't get it to work.
Thanks!
Mike
SET #Stime := '6:30PM';
IF ( CHAR_LENGTH(#Stime) = 6, SET #temp := 'one', SET #temp := 'two');
Assuming it's mySQL
The IF() function returns a value if a condition is TRUE, or another value if a condition is FALSE.
In your case this code will update temp with one or two depends on the length.
SET #Stime := '6:30PM';
SET #temp = IF(CHAR_LENGTH(#Stime) = 6,'one','two');

MySQL Variable Assignment via Procedure Not Working Correctly

In the code below, I'm trying go through the results of endDateTable row by row, comparing the current row's endDate to the previous row's endDate. If there has been any change since the previous, we increment #revisionNum. However, upon populating the new table, all of the #revisionNum entries are 0. What am I doing wrong?
NOTE: I'm using prepared statements in this manner since doing a straightforward SELECT into a variable gives a syntax error due to the LIMIT clause not allowing a variable in our version of MySQL.
BEGIN
DECLARE _currentEndDate DATETIME DEFAULT now();
DECLARE _priorEndDate DATETIME DEFAULT now();
SET #ResultsCount = (SELECT COUNT(*) FROM mainTable);
SET #j = 0;
WHILE #j < #ResultsCount DO
SET #revisionNum = 0;
/*CURRENT END DATE*/
SET #appResultQueryCurrent = CONCAT('
SELECT
end_date
INTO _currentEndDate
FROM endDateTable
LIMIT ', #j, ', 1'
);
PREPARE currentQueryStmt FROM #appResultQueryCurrent;
EXECUTE currentQueryStmt;
/*PREVIOUS END DATE*/
SET #appResultQueryPrior = CONCAT('
SELECT
end_date
INTO _priorAppEndDate
FROM endDateTable
LIMIT ', IF(#j = 0, 0, #j - 1), ', 1'
);
PREPARE priorQueryStmt FROM #appResultQueryPrior;
EXECUTE priorQueryStmt;
SET #revisionNum = IF(
#j = 0 OR (_currentEndDate = _priorEndDate),
#revisionNum,
IF(
_currentEndDate != _priorEndDate,
#revisionNum + 1,
#revisionNum
)
);
INSERT INTO finalTable (RevisionNum)
SELECT
#revisionNum AS RevisionNum
FROM endDateTable;
SET #j = #j +1;
END WHILE;
END $$
You don't need a loop, you can use INSERT INTO ... SELECT ..., incrementing the variable in the select query.
You also need an ORDER BY criteria to specify how to order the rows when comparing one row to the previous row.
INSERT INTO finalTable (RevisionNum, otherColumn)
SELECT revision, otherColumn
FROM (
SELECT IF(end_date = #prev_end_date, #revision, #revision := #revision + 1) AS revision,
#prev_end_date := end_date,
otherColumn
FROM endDateTable
CROSS JOIN (SELECT #prev_end_date := NULL, #revision := -1) AS vars
ORDER BY id) AS x
DEMO
The offset value in the LIMIT clause is tenuous without an ORDER BY.
Without an ORDER BY clause, MySQL is free to return results in any sequence.
There is no guarantee that LIMIT 41,1 will return the row before LIMIT 42,1, or that it won't return the exact same row as LIMIT 13,1 did.
(A table in a relational database represents an unordered set of tuples, there is no guaranteed "order" or rows in a table.)
But just adding ORDER BY to the queries isn't enough to fix the Rube-Goldberg-esque rigmarole.
In the code shown, it looks like each time through the loop, we're inserting a copy of endDateTable into finalTable. If that's 1,000 rows in endDateTable, we're going to get 1,000,000 rows (1,000 x 1,000) inserted into finalTable. Not at all clear why we need so many copies.
Given the code shown, it's not clear what the objective is. Looks like we are conditionally incrementing revisionNum, the end result of which is the highest revision num. Just guessing here.
If there is some kind of requirement to do this in a LOOP construct, within a procedure, I'd think we'd do a cursor loop. And we can use procedure variables vs user-defined variables.
Something along these lines:
BEGIN
DECLARE ld_current_end_date DATETIME;
DECLARE ld_prior_end_date DATETIME;
DECLARE li_done INT;
DECLARE li_revision_num INT;
DECLARE lcsr_end_date CURSOR FOR SELECT t.end_date FROM `endDateTable` t ORDER BY NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET li_done = TRUE;
SET li_done = FALSE;
SET li_revision_num = 0;
OPEN lcsr_end_date;
FETCH lcsr_end_date INTO ld_current_end_date;
SET ld_prior_end_date = ld_current_end_date;
WHILE NOT li_done DO
SET li_revision_num = li_revision_num + IF( ld_current_end_date <=> ld_prior_end_date ,0,1);
SET ld_prior_end_date := ld_current_end_date;
FETCH lcsr_end_date INTO ld_current_end_date;
END WHILE;
CLOSE lcsr_end_date;
INSERT INTO `finalTable` (revisionnum) VALUES (li_revision_num);
END $$
Note the "order by" clause on the SELECT, its not clear what the rows should be ordered on, so we're using a literal as a placeholder.
As the end result, we insert a single row into finalTable.
Again, it's not clear what the code in the question is supposed to achieve, but doing a cursor loop across ordered rows would be much more efficient than a bazillion dynamic SQL executions fetching individual rows.

Insert sequential numbers based on another field - MySQL

There is a similar question
Insert sequential number in MySQL
I want to insert sequential numbers to the table, but based on another field. I have two columns page_numner and parent, so the rows with same parent should have page_number as consequtive numbers. If parent changes, the page should start from 1 again and increase by one.
I was thinking to use smth like this
SELECT #i:=0;
SELECT #p:=0;
UPDATE my_table AS t SET page_number = CASE
WHEN #p = t.`parent` THEN #i:=#i+1
ELSE 1 -- assign current parent to #p ??
END
but, it cant figure out how to assign the new parent into #p for the else case.
Please note, that I am trying to achieve this with pure mysql (if possible of course)
Thanks
You can do what you want with this code:
set #p := -1;
set #i := 0;
UPDATE my_table t
SET page_number = (CASE WHEN #p = t.`parent` THEN #i := #i+ 1
WHEN (#p := t.parent) = NULL THEN NULL -- never happens
ELSE #i := 1
END)
ORDER BY t.parent;
Unfortunately, MySQL doesn't allow both ORDER BY and JOIN in the same UPDATE query. If it did, you could initialize the variables in the query.
Note the second condition just does the assignment. = NULL never returns TRUE.

Where is the error in this stored procedure

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".)