MYSQL Stored Procedures: Variable Declaration and Conditional Statements - mysql

I have looked over numerous tutorials, manuals and documentations, but I still can not get this to work.
I am trying to create a stored procedure using phpMyAdmin.
I cant seem to find the errors here, the sql errors are so vague...
CREATE PROCEDURE insertToonOneShot(IN locale CHAR(2), IN name VARCHAR(16), IN realm VARCHAR(24), IN faction CHAR(1), IN toon_level INT, IN class_name INT)
BEGIN
DECLARE #realmID INT;
DECLARE #classID INT;
DECLARE #toonID INT;
SET #realmID = SELECT id FROM realms WHERE realms.name = realm;
SET #classID = SELECT id FROM classes WHERE classes.name = class_name;
IF NOT #realmID IS NULL AND NOT #classID IS NULL AND #toonID IS NULL THEN
INSERT INTO
toon (`locale`, `name`, `realm_id`, `faction`, `level`, `class_id`)
VALUES
(locale, name, #realmID, faction, toon_level, #classID);
END IF;
END;
The error I am getting right now 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 #realmID INT; DECLARE #classID INT; DECLARE #toonID INT; SET #rea
at line 3
Probably one of the more frustrating things I have ever had to do...
I have seen many tutorials online that show using the # symbol in variable declaration, and others not using it, I have even seen some that use VAR instead of DECLARE. What is the right syntax?...

This question, and its answers, seem to be confusing session variables (which are prefixed with #) with procedural variables (which are not prefixed.) See the answers to this question for more information.
The accepted solution resolves the error by using session variables, but it could introduce problems related to variable scope. For example, if a variable called realmID has been defined outside the procedure, its value will be overwritten when the procedure is run.
The correct way to resolve the problem is to use only procedural variables. These are DECLAREd at the start of the procedure without the # prefix.
DELIMITER $$
CREATE PROCEDURE insertToonOneShot(
IN locale CHAR(2),
IN name VARCHAR(16),
IN realm VARCHAR(24),
IN faction CHAR(1),
IN toon_level INT,
IN class_name INT
)
BEGIN
DECLARE realmID INT;
DECLARE classID INT;
SELECT id INTO realmID FROM realms WHERE realms.name = realm LIMIT 1;
SELECT id INTO classID FROM classes WHERE classes.name = class_name LIMIT 1;
IF realmID IS NOT NULL AND classID IS NOT NULL THEN
INSERT INTO toon (`locale`, `name`, `realm_id`, `faction`, `level`, `class_id`)
VALUES (locale, name, realmID, faction, toon_level, classID);
END IF;
END$$
DELIMITER ;

When you have a subquery, it needs to have parentheses. These lines:
SET #realmID = SELECT id FROM realms WHERE realms.name = realm;
SET #classID = SELECT id FROM classes WHERE classes.name = class_name;
Should be:
SET #realmID = (SELECT id FROM realms WHERE realms.name = realm);
SET #classID = (SELECT id FROM classes WHERE classes.name = class_name);
Or, better yet, you don't need the set:
SELECT #realmID := id FROM realms WHERE realms.name = realm;
SELECT #classID := id FROM classes WHERE classes.name = class_name;

This does the trick:
CREATE PROCEDURE insertToonOneShot(IN locale CHAR(2), IN name VARCHAR(16), IN realm VARCHAR(24), IN faction CHAR(1), IN toon_level INT, IN class_name VARCHAR(12))
BEGIN
SELECT #realmID := id FROM realms WHERE realms.name = realm;
SELECT #classID := id FROM classes WHERE classes.name = class_name;
SELECT #toonID := id FROM toon WHERE toon.name = name AND toon.realm_id = #realmID;
IF NOT #realmID IS NULL AND NOT #classID IS NULL AND #toonID IS NULL
THEN
INSERT INTO toon (`locale`, `name`, `class_id`, `realm_id`, `faction`, `level`)
VALUES (locale, name, #classID, #realmID, faction, toon_level);
END IF;
END;
//
Apparently the declare statements were not required... Who would have known?
Thanks to Gordon Linoff for pointing me in the right direction.

Related

Stored procedure successful but doesn't run properly in mysql

I have a stored procedure about 2 tables like this:
CREATE DEFINER=`brambang`#`%` PROCEDURE `create_discount_campaign`(
IN discount_campaign_discount_type_id CHAR(22),
IN discount_campaign_product_id INT(10),
IN discount_campaign_marketing_target_id INT(10),
IN discount_campaign_max_use_per_user int(11),
IN discount_campaign_discount_code varchar(100),
IN discout_campaign_discount_amount decimal(10,0),
IN discount_campaign_start_date datetime,
IN discount_campaign_end_date datetime,
IN discount_campaign_min_order_quantity INT(11),
IN discount_campaign_min_order_price decimal(10,0),
IN discount_campaign_discount_quota INT(11),
IN discount_campaign_min_product_varian INT(11),
IN discount_campaign_apply_all_product INT(10),
IN discount_campaign_product_product_id int(10) unsigned,
IN discount_campaign_product_active tinyint(1),
IN discount_campaign_product_createdby int(11),
IN discount_campaign_product_updatedby int(11),
IN discount_campaign_product_category_id VARCHAR(100),
IN discount_campaign_advanced_discount_advanced_rules_id int(50),
IN discount_campaign_advanced_value varchar(255),
IN discount_campaign_advanced_status tinyint(4)
)
proc:
BEGIN
DECLARE is_dc_discount_type_id INT(10);
DECLARE is_dc_product_id INT(10);
DECLARE is_dc_marketing_target_id INT(10);
DECLARE is_dc_max_use_per_user INT(11);
DECLARE is_dc_discount_code varchar(100);
DECLARE is_dc_discount_amount decimal(10,0);
DECLARE is_dc_start_date datetime;
DECLARE is_dc_end_date datetime;
DECLARE is_dc_min_order_quantity INT(11);
DECLARE is_dc_min_order_price decimal(10,0);
DECLARE is_dc_discount_quota INT(11);
DECLARE is_dc_min_product_variant INT(11);
DECLARE is_dc_apply_all_product INT(10);
DECLARE is_dcp_product_id INT(10);
DECLARE is_dcp_active tinyint(1);
DECLARE is_dcp_createdby int(11);
DECLARE is_dcp_updatedby int(11);
DECLARE is_dca_discount_advanced_rules_id int(50);
DECLARE is_dca_value varchar(255);
DECLARE is_dca_status tinyint(4);
DECLARE LAST_INSERT_ID int(11);
DECLARE discount_campaign_product_category_id varchar(100);
SET #querie = NULL;
SET #querie2 = NULL;
SET #querie3 = NULL;
SET lc_time_names = "id_ID";
SET #is_dc_discount_type_id = NULL;
SET #is_dc_product_id = NULL;
SET #is_dc_marketing_target_id = NULL;
SET #is_dc_max_use_per_user = NULL;
SET #is_dc_discount_code = NULL;
SET #is_dc_discount_amount = NULL;
SET #is_dc_start_date = NULL;
SET #is_dc_end_date = NULL;
SET #is_dc_min_order_quantity = NULL;
SET #is_dc_min_order_price = NULL;
SET #is_dc_discount_quota = NULL;
SET #is_dc_min_product_variant = NULL;
SET #is_dc_apply_all_product = NULL;
SET #is_dcp_product_id = NULL;
SET #is_dcp_active = NULL;
SET #is_dcp_createdby = NULL;
SET #is_dcp_updatedby = NULL;
SET #is_dca_discount_advanced_rules_id = NULL;
SET #is_dca_value = NULL;
SET #is_dca_status = NULL;
SET #LAST_INSERT_ID = NULL;
SET #discount_campaign_product_category_id = NULL;
SET #is_dc_discount_type_id = discount_campaign_discount_type_id;
SET #is_dc_product_id = discount_campaign_product_id;
SET #is_dc_marketing_target_id = discount_campaign_marketing_target_id;
SET #is_dc_max_use_per_user = discount_campaign_max_use_per_user;
SET #is_dc_discount_code = discount_campaign_discount_code;
SET #is_dc_discount_amount = discout_campaign_discount_amount;
SET #is_dc_start_date = discount_campaign_start_date;
SET #is_dc_end_date = discount_campaign_end_date;
SET #is_dc_min_order_quantity = discount_campaign_min_order_quantity;
SET #is_dc_min_order_price = discount_campaign_min_order_price;
SET #is_dc_discount_quota = discount_campaign_discount_quota;
SET #is_dc_min_product_variant = discount_campaign_min_product_varian;
SET #is_dc_apply_all_product = discount_campaign_apply_all_product;
SET #discount_campaign_product_category_id = discount_campaign_product_category_id;
SET #querie = CONCAT("
INSERT INTO discount_campaigns (
discount_type_id,
product_id,
marketing_target_id,
max_use_per_user,
discount_code,
discount_amount,
start_date,
end_date,
min_order_quantity,
min_order_price,
discount_quota,
min_product_variant,
apply_all_products,
createdAt,
updatedAt
) VALUES (
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
?,
NOW(),
NOW()
);
");
PREPARE stmt FROM #querie;
EXECUTE stmt
USING
#is_dc_discount_type_id,
#is_dc_product_id,
#is_dc_marketing_target_id,
#is_dc_max_use_per_user,
#is_dc_discount_code,
#is_dc_discount_amount,
#is_dc_start_date,
#is_dc_end_date,
#is_dc_min_order_quantity,
#is_dc_min_order_price,
#is_dc_discount_quota,
#is_dc_min_product_variant,
#is_dc_apply_all_product;
DEALLOCATE PREPARE stmt;
SET #is_dcp_product_id = discount_campaign_product_product_id;
SET #is_dcp_active = discount_campaign_product_active;
SET #is_dcp_createdby = discount_campaign_product_createdby;
SET #is_dcp_updatedby = discount_campaign_product_updatedby;
SET #LAST_INSERT_ID = LAST_INSERT_ID();
IF (discount_campaign_apply_all_product = 0) THEN
SET #querie2 = CONCAT("INSERT INTO discount_campaign_product (discount_campaign_id,
product_id,
active,
createdby,
updatedby,
createdAt,
updatedAt
) SELECT ?,product_id,?,?,NULL,NOW(),NULL
FROM product_categories WHERE category_id IN
(?) AND status=1");
PREPARE stmt_2 FROM #querie2;
EXECUTE stmt_2
USING
#LAST_INSERT_ID,
#is_dcp_active,
#is_dcp_createdby,
#discount_campaign_product_category_id;
DEALLOCATE PREPARE stmt_2;
END IF;
END
basically i have 2 tables discount_campaign (any variable with an alias dc are this table) and discount_campaign_product (any variable with an alias dcp are this table). This stored procedure has a function to insert data into table discount_campaign with the parameter which been made. in discount_campaign table there's column namely apply_all_product. if apply_all_product filled "0" in parameter, then another parameter (with alias dcp) will insert some data in discount_campaign_product table. but when i applied with apply_all_product = 0, this store procedure only insert data in discount_campaign table, not in discount_campaign_product table.
Where is my mistake in this stored procedure?
I'd guess you didn't see any rows inserted into discount_campaign_product because there were no matching rows in product_categories. You used INSERT...SELECT FROM product_categories but you search for rows where category_id IN (?).
I suppose you mean this to turn into category_id IN (1,2,3,4,5) but that's not how query parameters work in SQL. One parameter can only be used for one scalar value. So in your code, it will search as if you did category_id IN ('1,2,3,4,5') which searches for a single string. It might match the first number in the string as it casts the string to a numeric, but if there is no category matching that first number, then it'll find no matches.
The workaround is to use FIND_IN_SET(category_id, '1,2,3,4,5'). This is really not how the function is supposed to be used, but it works to locate an integer in a comma-separated list of integers. Be careful not to put spaces in between the list elements, because it won't work with FIND_IN_SET().
Other issues:
You DECLARE local variables then you don't use them. Understand that in MySQL, the variables myvar and #myvar are two different variables. You need to declare as local variables the type with no # sigil. Whereas the type with the # sigil are user-defined session variables, which you don't declare -- just by setting its value, the variable is implicitly created.
You are using PREPARE/EXECUTE but there is no need to use dynamic SQL in this case. Your only dynamic parts are values, so you can just use the input parameters for the procedure as if they are query parameters. They are guaranteed to act like a single scalar, and they won't cause SQL injection. You would need to use PREPARE/EXECUTE only if you had dynamic expressions or dynamic identifiers in your queries, but you don't.
The only risk to using your procedure parameters directly in queries is that you should make sure the parameter names are not the same as any column names of tables in your query.
DECIMAL(10,0) doesn't make much sense. Why use decimal with 0 digits of scale? That's just an integer.
INT(11) vs INT(50) makes no difference. The argument to the INT types does not change their size or range of values. In fact, using this argument for INT types is deprecated in MySQL 8.0, so you should get into the habit of removing it.
Here's the procedure I tested, and it works:
CREATE PROCEDURE create_discount_campaign(
IN discount_campaign_discount_type_id CHAR(22),
IN discount_campaign_product_id INT,
IN discount_campaign_marketing_target_id INT,
IN discount_campaign_max_use_per_user INT,
IN discount_campaign_discount_code varchar(100),
IN discout_campaign_discount_amount decimal(10,0),
IN discount_campaign_start_date datetime,
IN discount_campaign_end_date datetime,
IN discount_campaign_min_order_quantity INT,
IN discount_campaign_min_order_price decimal(10,0),
IN discount_campaign_discount_quota INT,
IN discount_campaign_min_product_varian INT,
IN discount_campaign_apply_all_product INT,
IN discount_campaign_product_product_id INT UNSIGNED, -- this is unused?
IN discount_campaign_product_active TINYINT,
IN discount_campaign_product_createdby INT,
IN discount_campaign_product_updatedby INT,
IN discount_campaign_product_category_id VARCHAR(100),
IN discount_campaign_advanced_discount_advanced_rules_id INT,
IN discount_campaign_advanced_value varchar(255),
IN discount_campaign_advanced_status TINYINT
)
MODIFIES SQL DATA
BEGIN
DECLARE last_id BIGINT;
INSERT INTO discount_campaigns
SET
discount_type_id = discount_campaign_discount_type_id,
product_id = discount_campaign_product_id,
marketing_target_id = discount_campaign_marketing_target_id,
max_use_per_user = discount_campaign_max_use_per_user,
discount_code = discount_campaign_discount_code,
discount_amount = discout_campaign_discount_amount,
start_date = discount_campaign_start_date,
end_date = discount_campaign_end_date,
min_order_quantity = discount_campaign_min_order_quantity,
min_order_price = discount_campaign_min_order_price,
discount_quota = discount_campaign_discount_quota,
min_product_variant = discount_campaign_min_product_varian,
apply_all_products = discount_campaign_apply_all_product,
createdAt = NOW(),
updatedAt = NOW();
IF (discount_campaign_apply_all_product = 0) THEN
SET last_id = LAST_INSERT_ID();
INSERT INTO discount_campaign_product (discount_campaign_id,
product_id, active, createdby, updatedby, createdAt, updatedAt)
SELECT last_id, c.product_id, discount_campaign_product_active,
discount_campaign_product_createdby, NULL, NOW(), NULL
FROM product_categories AS c
WHERE FIND_IN_SET(c.category_id, discount_campaign_product_category_id)
AND c.status=1;
END IF;
END
Notice I used a different syntax for the first INSERT. This is a MySQL extension to standard SQL, and it only works for single-row inserts. But it's very handy, because it makes it easier. In standard INSERT syntax with the column list and then a VALUES clause, I find it's difficult to make sure I have matched the columns to the right values. The INSERT ... SET syntax is more readable.

Mysql Stored Proc not returning a VARCHAR out parameter

Below is my stored procedure. It works fine but my problem is I can't get the output parameter as VARCHAR.
The part where I'm having problem is the assignment of #curcName to the out parameter op_resultMessage
BEGIN
SET op_resultMessage = #curcName;
END;
Here's the Stored Procedure.
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCurriculum`(
IN p_curcName varchar(100),
IN p_description TEXT,
IN p_yearLevel VARCHAR(50),
IN p_syStart INT,
IN p_syEnd INT,
IN p_creator VARCHAR(50),
OUT op_resultMessage VARCHAR(50))
BEGIN
DECLARE curcName VARCHAR(20) ;
IF EXISTS
(SELECT #curcName := `name`
FROM curriculum
WHERE
yearLevel = p_yearLevel
AND syStart = p_syStart
AND syEnd = p_syEnd )
THEN --
BEGIN
SET op_resultMessage = #curcName;
END;
ELSE
BEGIN
INSERT INTO curriculum(`name`, description, yearLevel, syStart, syEnd, creator)
VALUES(p_curcName,p_description,p_yearLevel,p_syStart,p_syEnd,p_creator);
END;
END IF;
END
I'm trying to return a message IF name EXISTS
So it should go something like
SET op_resultMessage = #curcName 'already uses the school year and year level you're trying to insert';
But I don't know how to properly concatenate and assign values. I'm still confused with := SET and = operators. I guess that's where I'm having problems with.
If I change the out parameter's type to an INT like
OUT op_resultMessage VARCHAR(50)
then assigns a number to op_resultMessage like SET op_resultMessage = 1;
It returns the number 1 as out parameter values. It just won't work with varchar.
So when I try to call the procedure
CALL `enrollmentdb`.`addCurriculum`
('Test Curriculum ','Test ','Grade 1',2015,2016,'jordan',#outputMsg);
SELECT #outputMsg; -- this doesn't return any value even if Grade 1, 2015 and 2016 exists
I'd appreciate any help. I actually just learned mysql recently.
Thanks.
drop procedure if exists addCurriculum;
delimiter $$
CREATE PROCEDURE `addCurriculum`(
IN p_curcName varchar(100),
IN p_description TEXT,
IN p_yearLevel VARCHAR(50),
IN p_syStart INT,
IN p_syEnd INT,
IN p_creator VARCHAR(50),
OUT op_resultMessage VARCHAR(50))
BEGIN
DECLARE curcName VARCHAR(20) ;
SELECT `name` into #curcName
FROM curriculum
WHERE
yearLevel = p_yearLevel
AND syStart = p_syStart
AND syEnd = p_syEnd
LIMIT 1;
-- Note change above. When selecting into a variable (or more than 1)
-- then 0 or 1 rows can come back max or an Error occurs
IF #curcName is not null then
SET op_resultMessage = #curcName;
ELSE
BEGIN
INSERT INTO curriculum(`name`, description, yearLevel, syStart, syEnd, creator)
VALUES(p_curcName,p_description,p_yearLevel,p_syStart,p_syEnd,p_creator);
END;
SET op_resultMessage = 'GEEZ I am right here'; -- Drew added this
END IF;
END$$
delimiter ;
Note the commentary in the stored procedure, especially the part of only 0 or 1 rows returning else an Error will occur with a select into var pattern. So LIMIT 1. That may or may not be the row you want (limit 1), but that is where it is at right now.

declaring variable inside mysql stored procedure

we are trying to declare a variable inside mysql stored procedure that has transaction implemented in it. but it seems to be giving a syntax error :
following is the syntax of the stored procedure:
CREATE PROCEDURE `sp_MarkAppointmentRefferal`(
p_AppId bigint,
p_NewLocation bigint,
p_userId bigint,
p_ReferralReason varchar(500),
p_NewLocationName varchar(100)
)
begin
declare v_OldLocation int default 0;
set v_OldLocation = (select LocationId FROM appointments where iAppID = p_AppId limit 1 );
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
select -1;
END;
START TRANSACTION;
update table
set is_referred = 1,
referred_timestamp = now(),
referral_reason = p_ReferralReason
where iAppID = p_AppId
limit 1;
-- create a new appointment for the new referred location..
insert into appointments
(vAppName, vAppType, dAppDate, vCell, iPatID, iAppStatus, iuserid, iActive,
dInsertDate, iHSID, daily_ticket_no, LocationId, visit_id, encounter_id, ReferredFrom,ReferredOPDName, opd_name )
select vAppName, vAppType, now(), vCell, iPatID, iAppStatus, p_userId,
1, now(), iHSID, fn_GenerateNextAppointmentTicket(now(),p_NewLocation) , p_NewLocation, visit_id, encounter_id+1,
(select LocationId FROM appointments where iAppID = p_AppId limit 1),
(select OPD_Name FROM appointments where iAppID = p_AppId limit 1), p_NewLocationName
FROM appointments
where iAppID = p_AppId limit 1;
select LAST_INSERT_ID();
COMMIT;
end;
the syntax checker is saying that declare command is not valid here.
have also tried to place this inside the transaction clause and similar error shows up ..
any help is appreciated..
All declare statements should be at the top of the stored procedure body. Moving DECLARE EXIT HANDLER before the SET statement should fix the problem.

Default nvarchar parameter not respected

I have a stored procedure in SQL Server 2008 R2 with the following parameter values declared:
#UN nvarchar(30),
#SN nvarchar(8),
#GG uniqueidentifier,
#Ss irnapp.SymbolTableType READONLY,
#SD date,
#ED date,
#IR nvarchar(1),
#ID nvarchar(1),
#NR int = NULL,
#GP nvarchar(1) = N'N'
It was my intention that if the #GP value is not supplied, then it should be given a value of N'N'. However, the procedure only returns the expected results when I explicitly pass in N'N' for #GP.
I've attempted searching for examples of SQL stored procedures with default parameter values, but the only examples I've found for nvarchar are defaults of NULL, which is not feasible for my application.
Would anyone know if the above is a legal default parameter declaration?
UPDATE:
Thanks Aaron for the quick response. I was hoping this would be a simple catch, as the code is quite lengthy. That said, here goes:
BEGIN TRY
DECLARE #GI int;
EXEC irn.GetGroupID #UN, #SN, #GG, #GI OUT;
DECLARE #CUID int;
IF #GP = N'Y'
BEGIN
SELECT #CUID = UserID
FROM Users
WHERE Uname = #UN
AND SNum = #SN;
END;
DECLARE #NoteIDs irn.NoteIDTableType;
INSERT INTO #NIDs (NO, NID)
SELECT *
FROM GetNIDs(#GI, #Ss, #SD, #ED, #IR, #ID, #NR, #GP, #CUID);
EXEC GetNsByNIDs #NIDs, N'N';
END TRY
BEGIN CATCH
EXEC irn.CreateProcedureErrorLog
EXEC irn.RaiseProcedureError
END CATCH;
ALTER FUNCTION [i].[GetNIDs] (
#GID int,
#Ss SymbolTableType READONLY,
#SD date,
#ED date,
#IR nvarchar(1),
#ID nvarchar(1),
#NR int,
#GP nvarchar(1) = N'N',
#CUID int = NULL)
RETURNS #TopOrderedMatchingNote TABLE (
NO int NOT NULL PRIMARY KEY,
NID int NOT NULL UNIQUE)
AS
BEGIN
INSERT INTO #MN (NID)
SELECT NID
FROM N
WHERE GID = #GID
AND ND >= #FDate
AND ND <= #TDate
AND IP = #GP
AND ((IP = N'Y' AND CUID = #CUID) OR (IP = N'N'))
AND IsDeleted = CASE #IncludeDeleted
WHEN N'N' THEN N'N'
ELSE IsDeleted
END;
END;
...snip...
Hope this is helpful and thanks again
Yes, your default parameter declaration example is valid and legal. Here is a quick repro:
USE tempdb;
GO
CREATE PROCEDURE dbo.splunge
#GP nvarchar(1) = N'N'
AS
BEGIN
SET NOCOUNT ON;
SELECT COALESCE(#GP, N'Y');
END
GO
EXEC dbo.splunge;
EXEC dbo.splunge #GP = N'Y';
Results:
----
N
----
Y
If you're having problems getting this to work, you'll need to post more of your code to demonstrate what that means.

Porting MySQL Stored Procedure to Oracle

I'm trying to port a stored procedure from MySQL to Oracle, and I'm having a lot of trouble. I've gone through Oracle documentation, and I'm having trouble doing very basic things like declaring variables properly. I was hoping someone could show me how to properly declare and set variables.
My stored procedure is used to add values to two different tables and ensure that it's being mapped properly and the foreign keys aren't being violated.
Here is my MySQL Code:
CREATE DEFINER=root#% PROCEDURE proc_add_entry(IN theName vARCHAR(50), IN theKey VARCHAR(50), IN theOtherData VARCHAR(50), IN theOtherData2 INT, IN theStartDate DATE, IN theEndDate DaTE, IN theReferenceDate DaTE)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
BEGIN
declare theNameID int ;
declare theKeyID int ;
declare theOtherDataID int default null;
declare error bool default false;
declare continue handler for SQLEXCEPTION
set error = true;
set theKeyID = (select KeyID from map_alias ma where ma.alias = trim(theKey));
set theOtherDataID = (select theOtherDataID from map_otherdata mc where mc.otherdata = trim(theOtherData));
set theNameID = (select max(nameID) from inserttable);
set theNameID = theNameID + 1;
insert into inserttable values (theNameID , theKeyID , theOtherDataID , theOtherData2, theStartDate ,
theEndDate , theReferenceDate);
if error = true then
insert into errors_inserttable values (theNameID , theKeyID , theOtherDataID , theOtherData2, theStartDate ,
theEndDate , theReferenceDate);
end if;
set error = false;
insert into map_inserttable (theNameID , datasourceid, theName) values (theNameID , 1, theName);
if error = true then
insert into errors_map_inserttable (theNameID , datasourceid, theName) values (theNameID , 1, theName);
end if;
END
In Oracle, my last statement are being ignored (ORA-00922: Missing or invalid option). It should be a local variable, so I'm not sure why i'm getting that particular error.
I'm struggling to declare the continue handler as well. I'm getting the error:
Error(16,27): PLS-00103: Encountered the symbol "FOR" when expecting one of the following: := . ( # % ; not null range default character.
Here is my oracle code so far:
CREATE OR REPLACE PROCEDURE PROC_ADD_ENTRY
(
THENAME IN VARCHAR2
, THEKEY IN VARCHAR2
, THEOTHERDATA IN VARCHAR2
, THEOTHERDATA2 IN NUMBER
, THEFIRSTDATE IN DATE
, THELASTDATE IN DATE
, THEREFERENCEDATE IN DATE
) AS
THENAMEID INT;
THEKEYID INT;
THEOTHERDATAID int;
ERROR bool default false;
BEGIN
declare continue HANDLER FOR SQLEXCEPTION set error = true;
set THEKEYID = (select KEYID from map_INSERTTABLE mc where mc.Key = trim(THEKEY));
END PROC_ADD_ENTRY;
I'm sure this is stupidly simple for someone that uses oracle, but I'm reading the documentation and I'm seeing conflicting information on where and how to declare variables, continue handlers, and assign values to variables. (is it := or = to assign values? Do i use the word declare after the begin statement to declare variables, or do I do it the way I show below?)
If someone could show me:
a) where to declare a local variable
b) how to assign a value to it (i.e. 1 to an int)
c) how to assign a value from the DB to a variable (set var = select number from table_number tn where tn.number = 1)
d) how to declare a continue handler properly
I would really appreciate it.
You've go the basic structure fine.
create or replace procedure <name> (<param list>) as
<local variables>
begin
<body>
end <name>;
To address your specific questions:
a) where to declare a local variable
I've marked this section up above.
b) how to assign a value to it (i.e. 1 to an int)
You would use := for assignment.
eg. thenameid := 1;
The data type you want will typically match the sql data types (eg. NUMBER for the above) though there are PL/SQL-specific data types such as PLS_INTEGER. See the PL/SQL data types documentation for more details.
c) how to assign a value from the DB to a variable (set var = select number from table_number tn where tn.number = 1)
You would use the into keyword with a locally defined variable to store the value in. eg.
l_num_rows number;
select count(*) into l_num_rows from user_objects;
d) how to declare a continue handler properly
If I'm reading understanding your code correctly, you want set error = true to be executed every time there is a problem with an sql statement and then you want the stored procedure to carry on.
Exception handling is what you are after. You would wrap any or SQL PL/SQL statements that you think may have errors in an exception block like this, with as many exception cases as needed (eg. NO_DATA_FOUND):
begin
<statements that may fail>
exception when <exception name> then
<action>
...
exception when others then
<action>
end;
"other" is the catchall. You can have just this case handled but as with any error handling it is better practise to catch specific cases first.
For completion, here's roughly what your example procedure would look like. I've removed the error code flag as it is not needed and also changed the ints to numbers:
create or replace procedure proc_add_entry (
in thename varchar(50),
in thekey varchar(50),
in theotherdata varchar(50),
in theotherdata2 number,
in thestartdate date,
in theenddate date,
in thereferencedate date
) as
thenameid number;
thekeyid number;
theotherdataid number default null;
begin
begin
select keyid into thekeyid from map_alias ma where ma.alias = trim(thekey);
select theotherdataid into theotherdataid from map_otherdata mc where mc.otherdata = trim(theotherdata);
select max(nameid) into thenameid from inserttable;
thenameid := thenameid + 1;
insert into inserttable values (thenameid, thekeyid, theotherdataid, theotherdata2, thestartdate, theenddate, thereferencedate);
exception when others then
insert into errors_inserttable values (thenameid, thekeyid, theotherdataid, theotherdata2, thestartdate, theenddate, thereferencedate);
end;
begin
insert into map_inserttable (thenameid, datasourceid, thename) values (thenameid, 1, thename);
exception when others then
insert into errors_map_inserttable (thenameid, datasourceid, thename) values (thenameid, 1, thename);
end;
end proc_add_entry;