How to set up a WHILE loop with IF statement in MySQL? - mysql

I would like to create a stored routine for MySQL that figures out the number of business or working days for a month (Working Days are Monday thru Friday).
It's a syntax error however I don't know what the syntax error is. All it tells me is:
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 'WHILE(#daycount < #totaldays) DO IF (WEEKDAY(#checkweekday) < 6)
THEN ' at line 2
My Syntax Error is in the following:
WHILE(#daycount < #totaldays) DO
IF (WEEKDAY(#checkweekday) < 6) THEN
My Code:
SELECT MONTH(CURDATE()) INTO #curmonth;
SELECT MONTHNAME(CURDATE()) INTO #curmonthname;
SELECT DAY(LAST_DAY(CURDATE())) INTO #totaldays;
SELECT FIRST_DAY(CURDATE()) INTO #checkweekday;
SELECT DAY(#checkweekday) INTO #checkday;
SET #daycount = 0;
SET #workdays = 0;
BEGIN
WHILE(#daycount < #totaldays) DO
IF (WEEKDAY(#checkweekday) < 6) THEN
SET #workdays = #workdays+1;
END IF;
SET #daycount = #daycount+1;
SELECT ADDDATE('#checkweekday', INTERVAL 1 DAY) INTO #checkweekday;
END WHILE;
END;
SELECT #workdays;
Is someone able to assist?
UPDATE
I receive the same error with the following bit of code so it probably has something to do with this:
SET #workdays = 0;
IF (WEEKDAY('2013-06-13') < 6) THEN
SET #workdays = #workdays+1;
END IF;
SELECT #workdays;

I have discovered that you cannot have conditionals outside of the stored procedure in mysql. This is why the syntax error. As soon as I put the code that I needed between
BEGIN
SELECT MONTH(CURDATE()) INTO #curmonth;
SELECT MONTHNAME(CURDATE()) INTO #curmonthname;
SELECT DAY(LAST_DAY(CURDATE())) INTO #totaldays;
SELECT FIRST_DAY(CURDATE()) INTO #checkweekday;
SELECT DAY(#checkweekday) INTO #checkday;
SET #daycount = 0;
SET #workdays = 0;
WHILE(#daycount < #totaldays) DO
IF (WEEKDAY(#checkweekday) < 5) THEN
SET #workdays = #workdays+1;
END IF;
SET #daycount = #daycount+1;
SELECT ADDDATE(#checkweekday, INTERVAL 1 DAY) INTO #checkweekday;
END WHILE;
END
Just for others:
If you are not sure how to create a routine in phpmyadmin you can put this in the SQL query
delimiter ;;
drop procedure if exists test2;;
create procedure test2()
begin
select ‘Hello World’;
end
;;
Run the query. This will create a stored procedure or stored routine named test2. Now go to the routines tab and edit the stored procedure to be what you want. I also suggest reading http://net.tutsplus.com/tutorials/an-introduction-to-stored-procedures/ if you are beginning with stored procedures.
The first_day function you need is:
How to get first day of every corresponding month in mysql?
Showing the Procedure is working
Simply add the following line below END WHILE and above END
SELECT #curmonth,#curmonthname,#totaldays,#daycount,#workdays,#checkweekday,#checkday;
Then use the following code in the SQL Query Window.
call test2 /* or whatever you changed the name of the stored procedure to */
NOTE: If you use this please keep in mind that this code does not take in to account nationally observed holidays (or any holidays for that matter).

Related

MySQL Create Trigger Syntax Error (Last Line)

I'm creating a MySQL trigger designed to update various tables with a new value if certain values are changed by an UPDATE query. I keep receiving the following syntax error for this particular trigger:
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 '' at line 29
Line 29 in this case is the line of the END statement.
This is my full code:
DELIMITER $$
CREATE TRIGGER update_selling_prices BEFORE UPDATE ON t1
FOR EACH ROW
BEGIN
DECLARE update_price INT DEFAULT 0;
DECLARE selling_price_1 DECIMAL(10, 3) DEFAULT 0.000;
DECLARE selling_price_2 DECIMAL(10, 3) DEFAULT 0.000;
IF (OLD.rrp_price <> NEW.rrp_price OR OLD.discount_1 <> NEW.discount_1 OR OLD.discount_2 <> NEW.discount_2 OR OLD.net_price <> NEW.net_price OR OLD.markup <> NEW.markup OR OLD.delivery_cost <> NEW.delivery_cost) THEN
SET update_price = (SELECT b.is_auto_update FROM price_categories c INNER JOIN brands b ON b.brand_name = c.brand_name WHERE c.id = NEW.category_id LIMIT 1);
IF (update_price = 1) THEN
IF (NEW.is_bundle = 0) THEN
UPDATE t2 SET temp = 'Fired Single' WHERE id = NEW.id;
ELSE IF (NEW.is_bundle = 1) THEN
UPDATE t2 SET temp = 'Fired Bundle' WHERE id = NEW.id;
END IF;
END IF;
END IF;
END;
$$
DELIMITER ;
The current UPDATE statements are just placeholders for some actual calculations I'll end up doing.
Please note: I use Sequel Pro for most MySQL-related stuff and initially was using their GUI to try and add the trigger - it automatically adds the surrounding code so I would only create everything between the BEGIN and END statements. That also resulted in this same syntax error, so I don't believe it's related to the delimiters like some similar threads I've already found on here. Nevertheless, I've tried adding the full trigger code via a normal query; changing the delimiter syntax - for example END$$, END $$, END; $$ etc.
I've successfully created other triggers with similar syntax, but they do not include the DECLARE syntax.
Where am I going wrong?
The problem is here:
IF (NEW.is_bundle = 0) THEN
UPDATE t2 SET temp = 'Fired Single' WHERE id = NEW.id;
ELSE IF (NEW.is_bundle = 1) THEN
UPDATE t2 SET temp = 'Fired Bundle' WHERE id = NEW.id;
END IF;
Review documentation: https://dev.mysql.com/doc/refman/8.0/en/if.html
MySQL supports ELSEIF and this is different than ELSE IF. If you use ELSEIF, this continues the structure of the IF statement. If you use ELSE IF, it starts a new IF statement, so it should be like this:
IF (NEW.is_bundle = 0) THEN
UPDATE t2 SET temp = 'Fired Single' WHERE id = NEW.id;
ELSE
IF (NEW.is_bundle = 1) THEN
UPDATE t2 SET temp = 'Fired Bundle' WHERE id = NEW.id;
END IF;
END IF;
See that there is a complete IF/THEN/END IF statement within the ELSE block of the outer one?
But you didn't do that, so the END IF applies to the innermost IF statement, and then you're one level off for the rest of the body of the trigger.
When MySQL gets to the end of the whole CREATE TRIGGER statement, if there aren't enough ENDs to balance the blocks you began, MySQL complains with the error you saw.

Getting a syntax error in Mysql create procedure statement which comes from the script I have exported from another mysql server [duplicate]

This question already has answers here:
What is wrong with this stored procedure?
(2 answers)
Closed 3 years ago.
The following code generates an error saying there is a syntax error. I couldn't find any syntax error in this. Beside that, it cant have any syntax error since it has been imported in the form of script from another MySql server unless there is an update in the MySql language itself. if the error is due to the version difference, please can someone throw some light on how the code should be in the newer version.
CREATE DEFINER=`root`#`localhost` PROCEDURE `iterate_day` (IN `p1` TIME, IN `p2` TIME) BEGIN
SET #et = DATE_ADD(NOW(), INTERVAL 1 DAY);
SET #currt = DATE_ADD(NOW(), INTERVAL 1 DAY);
SET #lastday = DATE_ADD(NOW(), INTERVAL 30 DAY);
SET #startHour = p1;
SET #currHour = p1;
SET #endHour = p2;
WHILE #currt<=#lastday DO
WHILE #currHour < #endHour DO
SET #currHour = ADDTIME(#currHour,'00:30:00');
INSERT INTO `blooddb`.`appointments` (`Date`, `timeStart`, `timeEnd`, `status`, `client_id`, `app_id` ) VALUES (#currt, #startHour, #currHour, 1, NULL, NULL) ON DUPLICATE KEY UPDATE `date` = `date` ;
END WHILE;
SET #currt = DATE_ADD(#currt, INTERVAL 1 DAY);
SET #startHour = p1;
SET #currHour = p1;
END WHILE;
END
The error:
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 2
Can someone indicate whats wrong in the code
To avoid MySQL thinking the ; within the procedures are ending the create procedure, you need to change the delimter for the end of a statement. Currently it is ;. You can set it to literally anything else.
If you use the mysql client program to define a stored program containing semicolon characters, a problem arises. By default, mysql itself recognizes the semicolon as a statement delimiter, so you must redefine the delimiter temporarily to cause mysql to pass the entire stored program definition to the server.
The MySQL documentation suggests //.
Don't forget to set it back to ;.
delimiter //
CREATE DEFINER=`root`#`localhost` PROCEDURE `iterate_day` (IN `p1` TIME, IN `p2` TIME) BEGIN
SET #et = DATE_ADD(NOW(), INTERVAL 1 DAY);
...
END
//
delimter ;

Sql trigger's query

I am trying to create custom id for my table in the following format
2 random alphabets - 00MaxID
for e.g: AA-001
I have tried writing a query for it but it is not working, this is my first time writing a trigger also writing a complex query such as this.
UPDATED-2
the following query gives me an error near "SELECT count(cus_id) INTO #ct FROM customer;"
CREATE
TRIGGER `id_gen` BEFORE INSERT
ON `testdb`.`customer`
FOR EACH ROW BEGIN
SELECT count(cus_id) INTO #ct FROM customer;
IF #ct < 1000 THEN
SET #cs_id = LPAD(#ct+1, 3, 0 );
ELSE
SET #cs_id = #ct+1;
END IF;
SET NEW.cus_id = CONCAT(CHAR(FLOOR(65 + RAND() * 26),FLOOR(65 + RAND() * 26)),'-',#cs_id);
END;
Error
SQL query: Documentation
CREATE
TRIGGER `id_gen` BEFORE INSERT
ON `testdb`.`customer`
FOR EACH ROW BEGIN
SELECT count(cus_id) INTO #ct FROM customer
MySQL said: Documentation
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 6
Executing with Trigger Section
Answered by #Solarflare.
In the screenshot, you can see that phpmyadmin automatically added code including for each row, which is now twice in the statement (thus the error). Your own code will start with begin.
BEGIN
SELECT count(cus_id)+1 INTO #ct FROM customer;
IF #ct < 1000 THEN
SET #cs_id = LPAD(#ct, 3, 0 );
ELSE
SET #cs_id = #ct;
END IF;
SET NEW.cus_id = CONCAT(CHAR(FLOOR(65 + RAND() * 26),FLOOR(65 + RAND() * 26)),'-',#cs_id);
END;

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;

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;