mysql do while routine erring - mysql

MySQL v5.6.17. I am getting an incorrect syntax error on the first line when stepping through this (generic error 1064 - 'Script line: 33 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'declare x int' at line 1
"). I have looked at examples of other do-whiles but step through execution of this code hasn't even made it that far. Very puzzled. I would also appreciate any suggestions on whether this do-while method is most efficient. Essentially I need to loop through split_col_table thousands of rows looking at DelimitedCol column which hold delimited values, and remove each delimited value into a separate ROW into report, then I am deleting that previous value and the delimiter from split_col_table until there are no delimiters left. At the end I will make a final pass to get the remainder of the values out of DelimitedCol in split_col_table.
Code relevant to the loop:
declare x int;
set x = select count(*) from split_col_table where DelimitedCol like '%;%';
while x > 0 DO
INSERT INTO report (SELECT id, val1,LEFT(DelimitedCol,LOCATE(';',DelimitedCol)-1) FROM split_col_table where DelimitedCol like '%;%');
UPDATE split_col_table SET DelimitedCol = RIGHT(DelimitedCol, LENGTH(DelimitedCol)- LOCATE(';',DelimitedCol)) where DelimitedCol > '';
#repeat until no more ';' in split_col_table
set x = select count(*) from split_col_table where DelimitedCol like '%;%';
end while;

Related

MySQL Stored Procedure - increment a variable

Why is my stored procedure erroring on the last two lines. Am i missing something? I am trying to write something that takes my basis name and creates a variable that increments it by 1 and continues until it hits 150. I know how to do this in Microsft SQL which might be where my syntax errors are coming from. Thanks.
USE repo;
DELIMITER $$
Create DEFINER = 'root' # 'localhost' procedure csvOutput()
BEGIN DECLARE basis_name int; SET #basis_name = 0;
if basis_name < 150 then
set basis_name = basis_name + 1;
SELECT *
FROM
(SELECT
'cbt.component',
'fl.input_flow',
'ft.flow',
'Function_ID',
'sf.subfunction',
'fl.output_flow',
'ft2.flow',
'# of times it appears',
'sf.tier'
UNION ALL (SELECT
cbt.component,
fl.input_flow,
ft.flow,
sf.id AS 'Function ID',
sf.subfunction,
fl.output_flow,
ft2.flow,
COUNT(a.name) AS '# of times it appears',
sf.tier
FROM
artifact a, function fu, flow fl, flow_type ft, subfunction_type sf, flow_type ft2, comp_basis_type cbt
WHERE
a.id = fu.describes_artifact
AND fu.id = fl.describes_function
AND fl.input_flow = ft.id
AND fl.output_flow = ft2.id
AND fu.subfunction_type = sf.id
AND a.basis_name = 115
AND fu.supporting = 0
AND a.basis_name = cbt.id
AND sf.tier >= 3
GROUP BY fl.input_flow
ORDER BY COUNT(fu.id) DESC)) resulting_set INTO OUTFILE 'C:/ProgramData/MySQL/MySQL Server 5.7/Uploads/test4.csv' FIELDS ENCLOSED BY '"' TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '';
END$$
Delimiter;
I tested your statement. For future reference, when you ask about an error on Stack Overflow, please include the error.
This is the error I got from testing:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE basis_name int; SET #basis_name = 0;
I see the problem, it's actually on this line:
USE repo; DELIMITER $$ Create DEFINER = 'root' # 'localhost' procedure csvOutput()
Keep in mind that DELIMITER sets the statement delimiter to all of the following text until end of line. So you have set the delimiter not to $$, but to the entire string: $$ Create DEFINER = 'root' # 'localhost' procedure csvOutput()
The error reports some problem with DECLARE because it's trying to start a new statement, not part of the procedure. BEGIN TRANSACTION is a legal statement, but other than that, BEGIN cannot be used on its own in MySQL. So BEGIN DECLARE... caused MySQL to say, "whoah, that's not what I expect you to add after BEGIN!"
Just add a newline after DELIMITER $$
Also note that you must not use a semicolon after the DELIMITER statement because that will become part of your statement terminator too. How else could you set the delimiter back to ;?
Another issue that would help you in the future, but it is not the root cause of the error you asked about:
In MySQL, basis_name and #basis_name are two different variables.
Variables declared with DECLARE within a procedure don't have the # prefix. You use them without the prefix when you declare them and when you use them. These variables are local to the stored procedure where they are declared.
Variables with the # prefix don't need to be declared. These are called "user-defined variables". You can create them merely by assigning a value to an identifier. You must always use the # prefix when you reference these variables. These variables are not local to the procedure where you set them. They retain their value in your current session after the procedure has finished. You can set their value and read their value with simple SQL statements outside a procedure.
This is a common issue on which Microsoft users get confused when moving to MySQL.

Loops in stored procedures that won't execute

I've read many threads here about mysql loops that throw errors. So do mine but neither of the solutions I've tried seem to fix my problem.
I'm working with some stored procedures and I'm trying to get the following loop to work:
DECLARE counter INT(11) DEFAULT 0;
add_rows: LOOP
INSERT INTO vote_orders (id, vote_id, vote_order, vote_candidate)
WHILE counter <= #number_of_candidates DO
SELECT NULL, vote_id, counter, vote_candidate_a
FROM votes_copy
IF counter = #number_of_candidates THEN
LEAVE add_rows;
END IF;
UNION
END WHILE;
END LOOP add_rows;
This always throws the following error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE counter INT(11) DEFAULT 0;
add_rows: LOOP
INSERT INTO vote_orde' at line 30
I've tried to only store the declaration of 'counter' which seems to work perfect but as soon as I try to add the loop it fails. (#number_of_candidates is a stored procedure that's called from the loop, the UNION is supposed to union each selects until counter reaches #number_of_candidates).
Do you have any suggestions on why this won't work?

MySQL Syntax : "the right syntax to use near" - right in the beginning

I am a MySQL-noob and today I tried to setup a MySQL call which is more than 5 lines long. I keep getting syntax errors which I try to fix for hours, but I don't have a clue what the problem is. Here is the code:
USE myDatabase;
DELIMITER //
CREATE PROCEDURE MYPROC()
BEGIN
SET #ID = 1;
SET #maxID = 3;
CREATE TEMPORARY TABLE resultTable(v DOUBLE, ttc DOUBLE);
WHILE (#ID < #maxID) DO
INSERT partTable1.v, partTable2.ttc
INTO
resultTable
FROM
(SELECT * FROM
(((SELECT time_sec, v FROM speedTable WHERE (trip_id = #ID)) as partTable1)
INNER JOIN
((SELECT time_sec, ttc FROM sightsTable WHERE (trip_id = #ID)) as partTable2) ON
(0.04 > abs(partTable1.time_sec - partTable2.time_sec)))
);
SET #ID := #ID + 1;
END WHILE;
END //
DELIMITER;
CALL MYPROC();
SELECT * FROM resultTable LIMIT 100;
Is there anything obvious that needs to be corrected?
Update1: Added semicolon to the "CREATE.."-statement, now first three statements are OK.
Update2: Added 3 more semicolons!
Update3: Followed the suggestion to make it a function + separate function call. Error message changed!
Update4: I fixed the issues mentioned in the two answers. Still something wrong there. See updated code above and error message below.
Updated error message:
ERROR 1064 (42000) at line 4: You have an error in your SQL syntax; check the ma
nual that corresponds to your MySQL server version for the right syntax to use n
ear ' partTable2.ttc
INTO
resultTable
FROM
(SELECT * FROM
(((SELE' at line 11
Kind Regards,
Theo
Flow control statements, of which WHILE is one, can only be used within a stored procedure, but you are attempting to use it as a plain query via the console.
If you absolutely must take this path (using mysql instead of an application language), create a store procedure with the code you want, then call it.
Creating the procedure would look like this:
DELIMITER //
CREATE PROCEDURE MYPROC()
BEGIN
WHILE (#ID < #maxID) DO
SET #partTable1 = (SELECT time_sec, v FROM speedTable WHERE (trip_id = #ID));
SET #partTable2 = (SELECT time_sec, ttc FROM sightsTable WHERE (trip_id = #ID));
INSERT v, ttc INTO resultTable FROM
(#partTable1 INNER JOIN #partTable2 ON
(0.04 > abs(partTable1.time_sec - partTable2.time_sec)));
SET #ID := #ID + 1;
END WHILE;
END//
DELIMITER ;
Then to call it:
CALL MYPROC();
See this SQLFiddle of a simplified version of this working.
Note that you do have one syntax error:
#ID = #ID + 1; -- incorrect syntax
SET #ID := #ID + 1; -- correct
Still some syntactic problems and functionality problems...
You can't use WHILE in SQL scripts. You can use WHILE only in the body of a stored routine. See http://dev.mysql.com/doc/refman/5.6/en/flow-control-statements.html
You can't use SET to assign multiple columns to a scalar. MySQL doesn't support relation-valued variables, only scalar variables. See http://dev.mysql.com/doc/refman/5.6/en/set-statement.html
You can INSERT from the results of a query with a join, but the query must be introduced with SELECT. See http://dev.mysql.com/doc/refman/5.6/en/insert-select.html
You can't use session variables as the names of tables. You would have to use a prepared statement. See http://dev.mysql.com/doc/refman/5.6/en/prepare.html But that opens a whole different can of worms, and doing it wrong can be a security vulnerability (see http://xkcd.com/327). I wouldn't recommend you start using prepared statements as a self-described MySQL-noob.
This problem is probably simpler than you're making it. You don't need a temporary table, and you don't need to read the results one row at a time.
Here's an example that I think does what you intend:
USE myDatabase
SET #ID = 1;
SET #maxID = 3;
SELECT sp.v, si.ttc
FROM speedTable AS sp
INNER JOIN sightsTable AS si
ON (sp.trip_id = si.trip_id AND 0.04 > ABS(sp.time_sec - si.time_sec))
WHERE sp.trip_id BETWEEN #ID AND #maxID;

Lua & SQL Syntax Error

I'm trying both of these ways to run a SQL query with Lua using the GMod mysqloo library.
Each query is complaining about the line with the EXISTS() having an SQL syntax error.
local SQL6 = [[INSERT INTO accounts(UniqueID,Money)
VALUES(]]..UniqueID..[[,]]..StartingCash..[[)
WHERE NOT EXISTS(SELECT 0 FROM accounts WHERE UniqueID=]]..UniqueID..[[)]]
local SQL7 = [[IF Not EXISTS (SELECT * FROM accounts WHERE UniqueID=']]..UniqueID..[[')
BEGIN
INSERT INTO accounts(UniqueID,Money)
VALUES(]]..UniqueID..[[,]]..StartingCash..[[)
END
ELSE
BEGIN
SELECT * FROM accounts WHERE UniqueID=]]..UniqueID..[[
END]]
I don't know SQL but it seems that:
you need quotes around UniqueID in line 3 and 11, as in line 4.
you probably need a space or newline before the last END.

Creating a database function within Magento for a module

I have a working module in Magento that is modeled after some custom code we use outside of our install. This module currently add 5 tables to the database to store info and I have extended the Admin to CRUD the info. The ultimate goal here is to move the majority of this custom programming into Magento.
Currently our custom code sits outside Magento and hits a separate database. This database has those same 5 tables, a stored procedure, and 4 functions. What I would like to do now is move the stored procedures and the functions into Magento's database and change the custom code to call all of it's data from Magento's db. However, I can't seem to figure out how the "CREATE FUNCTION" call should be set up for Magento to execute it properly.
The SQL I am using is:
DROP FUNCTION IF EXISTS {$this->getTable('fn_Get_HardinessZone')};
CREATE FUNCTION {$this->getTable('fn_Get_HardinessZone')}(IN ZipCode varchar()) RETURNS integer AS
DECLARE Result integer;
BEGIN
SELECT MAX(Zone) into Result
FROM AMI_zones
WHERE (Hfzip <= LEFT(ZipCode, 5)) AND (Htzip >= LEFT(ZipCode, 5));
if Result is null or Result < 1 or (Result > 11 and Result <> 99) Then
/*if the left most character is alpha, then set the zone to 98 for Canada*/
if Left(zipCode, 1) >= 'A' and LEFT(zipcode,1) <= 'Z' THEN
set result = 98;
else
set Result = 99;
End if;
END if;
RETURN Result;
END;
But this always generates the following error:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IN ZipCode varchar()) RETURNS'
So what is the proper way to format a SQL call to be run in a module's install/update script to insert a function or stored procedure into Maganeto's database?
The problem is with your SQL statement:
You have an error in your SQL syntax; check the manual ...
for the right syntax to use near
'IN ZipCode varchar()) RETURNS'
I would recommend running the SQL through PhpMyAdmin or on the command line until you get it right, then run it through Magento. This man page describes the syntax of CREATE FUNCTION: http://dev.mysql.com/doc/refman/5.0/en/create-procedure.html. When testing stored procedures /functions in the mysql client (or PhpMyAdmin) be sure to change the delimiter so that the semicolons in your function body are interpreted correctly.
The below SQL worked for me. The things I changed from your original statement are:
IN is not allowed in function declarations (IN ZipCode varchar())
I was required to explicitly state length of the varchar
The DECLARE belongs inside the function
I am guessing that your function is DETERMINISTIC, meaning it will always produce the same results for the same input parameters. If this is not the case, remove DETERMINISTIC from the RETURNS line
Give this a shot:
DROP FUNCTION IF EXISTS {$this->getTable('fn_Get_HardinessZone')};
CREATE FUNCTION {$this->getTable('fn_Get_HardinessZone')} (ZipCode VARCHAR(15))
RETURNS INTEGER DETERMINISTIC
BEGIN
DECLARE result INTEGER;
SELECT MAX(Zone) INTO result
FROM AMI_zones
WHERE (Hfzip <= LEFT(ZipCode, 5)) AND (Htzip >= LEFT(ZipCode, 5));
IF result IS NULL OR result < 1 OR (result > 11 AND result <> 99) THEN
/* if the left most character is alpha, then set the zone to 98 for Canada */
IF LEFT(ZipCode, 1) >= 'A' AND LEFT(ZipCode, 1) <= 'Z' THEN
SET result = 98;
ELSE
SET result = 99;
END IF;
END IF;
RETURN result;
END;