<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://community.appian.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures</link><description /><dc:language>en-US</dc:language><generator>Telligent Community 12</generator><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures</link><pubDate>Tue, 23 Apr 2024 14:42:12 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Current Revision posted to Guide by Appian Max Team on 4/23/2024 2:42:12 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you&amp;#39;re using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));

&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It&amp;#39;ll still work, but it&amp;#39;ll write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you&amp;#39;ll need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="more_resources"&gt;More Resources&lt;/h2&gt;
&lt;p&gt;For more advanced error handling and MariaDB documentation, consult the following resources:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-handler/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE HANDLER&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Construct to declare how errors are handled.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/sqlstate/"&gt;&lt;span style="font-weight:400;"&gt;SQLSTATE&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: A string that identifies a condition&amp;rsquo;s class and subclass.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/diagnostics-area/"&gt;&lt;span style="font-weight:400;"&gt;Diagnostics Area&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: The diagnostics area contains information about the error conditions produced by an SQL statement, as well as some information about the statement that generated them.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/get-diagnostics/"&gt;&lt;span style="font-weight:400;"&gt;GET DIAGNOSTICS&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Copy information about the diagnostics area into variables.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-condition/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE CONDITION&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: For declaring a named error condition.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/signal/"&gt;&lt;span style="font-weight:400;"&gt;SIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: May be used to produce a custom error message.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/resignal/"&gt;&lt;span style="font-weight:400;"&gt;RESIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Used to send a SIGNAL again for the previous error.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/mariadb-error-codes/"&gt;&lt;span style="font-weight:400;"&gt;MariaDB Error Code Reference&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: MariaDB shares error codes with MySQL, while also adding a number of new error codes specific to MariaDB.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Data Architecture, Architecture&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/14</link><pubDate>Wed, 06 Mar 2024 16:01:17 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 14 posted to Guide by joel.larin on 3/6/2024 4:01:17 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you&amp;#39;re using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));

&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It&amp;#39;ll still work, but it&amp;#39;ll write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you&amp;#39;ll need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="more_resources"&gt;More Resources&lt;/h2&gt;
&lt;p&gt;For more advanced error handling and MariaDB documentation, consult the following resources:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-handler/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE HANDLER&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Construct to declare how errors are handled.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/sqlstate/"&gt;&lt;span style="font-weight:400;"&gt;SQLSTATE&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: A string that identifies a condition&amp;rsquo;s class and subclass.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/diagnostics-area/"&gt;&lt;span style="font-weight:400;"&gt;Diagnostics Area&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: The diagnostics area contains information about the error conditions produced by an SQL statement, as well as some information about the statement that generated them.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/get-diagnostics/"&gt;&lt;span style="font-weight:400;"&gt;GET DIAGNOSTICS&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Copy information about the diagnostics area into variables.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-condition/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE CONDITION&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: For declaring a named error condition.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/signal/"&gt;&lt;span style="font-weight:400;"&gt;SIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: May be used to produce a custom error message.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/resignal/"&gt;&lt;span style="font-weight:400;"&gt;RESIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Used to send a SIGNAL again for the previous error.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/mariadb-error-codes/"&gt;&lt;span style="font-weight:400;"&gt;MariaDB Error Code Reference&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: MariaDB shares error codes with MySQL, while also adding a number of new error codes specific to MariaDB.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Data Architecture, Architecture&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/13</link><pubDate>Thu, 22 Feb 2024 18:11:25 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 13 posted to Guide by Appian Max Team on 2/22/2024 6:11:25 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you&amp;#39;re using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));

&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It&amp;#39;ll still work, but it&amp;#39;ll write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you&amp;#39;ll need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="more_resources"&gt;More Resources&lt;/h2&gt;
&lt;p&gt;For more advanced error handling and MariaDB documentation, consult the following resources:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-handler/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE HANDLER&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Construct to declare how errors are handled.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/sqlstate/"&gt;&lt;span style="font-weight:400;"&gt;SQLSTATE&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: A string that identifies a condition&amp;rsquo;s class and subclass.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/diagnostics-area/"&gt;&lt;span style="font-weight:400;"&gt;Diagnostics Area&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: The diagnostics area contains information about the error conditions produced by an SQL statement, as well as some information about the statement that generated them.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/get-diagnostics/"&gt;&lt;span style="font-weight:400;"&gt;GET DIAGNOSTICS&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Copy information about the diagnostics area into variables.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-condition/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE CONDITION&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: For declaring a named error condition.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/signal/"&gt;&lt;span style="font-weight:400;"&gt;SIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: May be used to produce a custom error message.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/resignal/"&gt;&lt;span style="font-weight:400;"&gt;RESIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Used to send a SIGNAL again for the previous error.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/mariadb-error-codes/"&gt;&lt;span style="font-weight:400;"&gt;MariaDB Error Code Reference&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: MariaDB shares error codes with MySQL, while also adding a number of new error codes specific to MariaDB.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Data Architecture, Architecture&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/12</link><pubDate>Thu, 22 Feb 2024 18:10:25 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 12 posted to Guide by Appian Max Team on 2/22/2024 6:10:25 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you&amp;#39;re using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));

&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It&amp;#39;ll still work, but it&amp;#39;ll write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you&amp;#39;ll need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="more_resources"&gt;More Resources&lt;/h2&gt;
&lt;p&gt;For more advanced error handling and MariaDB documentation, consult the following resources:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-handler/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE HANDLER&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Construct to declare how errors are handled.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/sqlstate/"&gt;&lt;span style="font-weight:400;"&gt;SQLSTATE&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: A string that identifies a condition&amp;rsquo;s class and subclass.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/diagnostics-area/"&gt;&lt;span style="font-weight:400;"&gt;Diagnostics Area&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: The diagnostics area contains information about the error conditions produced by an SQL statement, as well as some information about the statement that generated them.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/get-diagnostics/"&gt;&lt;span style="font-weight:400;"&gt;GET DIAGNOSTICS&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Copy information about the diagnostics area into variables.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-condition/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE CONDITION&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: For declaring a named error condition.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/signal/"&gt;&lt;span style="font-weight:400;"&gt;SIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: May be used to produce a custom error message.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/resignal/"&gt;&lt;span style="font-weight:400;"&gt;RESIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Used to send a SIGNAL again for the previous error.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/mariadb-error-codes/"&gt;&lt;span style="font-weight:400;"&gt;MariaDB Error Code Reference&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: MariaDB shares error codes with MySQL, while also adding a number of new error codes specific to MariaDB.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Data Architecture, Architecture&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/11</link><pubDate>Thu, 22 Feb 2024 18:08:20 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 11 posted to Guide by Appian Max Team on 2/22/2024 6:08:20 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you&amp;#39;re using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));

&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It&amp;#39;ll still work, but it&amp;#39;ll write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you&amp;#39;ll need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="more_resources"&gt;More Resources&lt;/h2&gt;
&lt;p&gt;For more advanced error handling and MariaDB documentation, consult the following resources:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-handler/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE HANDLER&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Construct to declare how errors are handled.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/sqlstate/"&gt;&lt;span style="font-weight:400;"&gt;SQLSTATE&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: A string that identifies a condition&amp;rsquo;s class and subclass.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/diagnostics-area/"&gt;&lt;span style="font-weight:400;"&gt;Diagnostics Area&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: The diagnostics area contains information about the error conditions produced by an SQL statement, as well as some information about the statement that generated them.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/get-diagnostics/"&gt;&lt;span style="font-weight:400;"&gt;GET DIAGNOSTICS&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Copy information about the diagnostics area into variables.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-condition/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE CONDITION&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: For declaring a named error condition.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/signal/"&gt;&lt;span style="font-weight:400;"&gt;SIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: May be used to produce a custom error message.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/resignal/"&gt;&lt;span style="font-weight:400;"&gt;RESIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Used to send a SIGNAL again for the previous error.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/mariadb-error-codes/"&gt;&lt;span style="font-weight:400;"&gt;MariaDB Error Code Reference&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: MariaDB shares error codes with MySQL, while also adding a number of new error codes specific to MariaDB.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Data Architecture, Architecture&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/10</link><pubDate>Thu, 22 Feb 2024 18:02:24 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 10 posted to Guide by Appian Max Team on 2/22/2024 6:02:24 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));

&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It will still work, but it will write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you will need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="more_resources"&gt;More Resources&lt;/h2&gt;
&lt;p&gt;For more advanced error handling and MariaDB documentation, consult the following resources:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-handler/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE HANDLER&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Construct to declare how errors are handled.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/sqlstate/"&gt;&lt;span style="font-weight:400;"&gt;SQLSTATE&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: A string that identifies a condition&amp;rsquo;s class and subclass.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/diagnostics-area/"&gt;&lt;span style="font-weight:400;"&gt;Diagnostics Area&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: The diagnostics area contains information about the error conditions produced by an SQL statement, as well as some information about the statement that generated them.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/get-diagnostics/"&gt;&lt;span style="font-weight:400;"&gt;GET DIAGNOSTICS&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Copy information about the diagnostics area into variables.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/declare-condition/"&gt;&lt;span style="font-weight:400;"&gt;DECLARE CONDITION&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: For declaring a named error condition.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/signal/"&gt;&lt;span style="font-weight:400;"&gt;SIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: May be used to produce a custom error message.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/resignal/"&gt;&lt;span style="font-weight:400;"&gt;RESIGNAL&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: Used to send a SIGNAL again for the previous error.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;a href="https://mariadb.com/kb/en/mariadb-error-codes/"&gt;&lt;span style="font-weight:400;"&gt;MariaDB Error Code Reference&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;: MariaDB shares error codes with MySQL, while also adding a number of new error codes specific to MariaDB.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/9</link><pubDate>Thu, 22 Feb 2024 17:44:54 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 9 posted to Guide by Appian Max Team on 2/22/2024 5:44:54 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));

&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It will still work, but it will write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you will need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/8</link><pubDate>Thu, 22 Feb 2024 17:44:28 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 8 posted to Guide by Appian Max Team on 2/22/2024 5:44:28 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It will still work, but it will write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you will need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/7</link><pubDate>Thu, 22 Feb 2024 17:43:32 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 7 posted to Guide by Appian Max Team on 2/22/2024 5:43:32 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It will still work, but it will write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="powershell"&gt;MariaDB [AppianAnywhere]&amp;gt; select * from errorhandling;
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
| id | procedureName | errorText                                          | errorNumber | sqlState | created             |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
|  1 | exampleProc   | Warning: Data truncated for column &amp;#39;name&amp;#39; at row 1 |        1265 | 01000    | 2023-04-14 16:46:56 |
+----+---------------+----------------------------------------------------+-------------+----------+---------------------+
1 row in set (0.000 sec)
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you will need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/6</link><pubDate>Thu, 22 Feb 2024 17:41:48 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 6 posted to Guide by Appian Max Team on 2/22/2024 5:41:48 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException Handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlwarning_handling"&gt;SQLWarning Handling&lt;/h2&gt;
&lt;p&gt;If you also want to capture warnings while your procedure is running, add the following SQL statements after the SQLException section:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Warning handling
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # Set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“Warning: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="putting_it_all_together"&gt;Putting it All Together&lt;/h2&gt;
&lt;p&gt;Be careful when copying and pasting. Quotes and spaces may get converted between this document and a terminal, potentially breaking the query.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Create procedure for inserting data into test table:
DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN

  # Exception handling
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
    @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“ERROR: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Warning handling
  DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    # Set procedure name variable for logging
    SET @proc_name = ‘exampleProc’;
    SET @error_text = CONCAT(“Warning: “, @text);
    INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
  END;

  # Procedure body
  INSERT INTO test SELECT p_text;

END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="testing"&gt;Testing&lt;/h2&gt;
&lt;p&gt;Call the example procedure with a value that fits into the column. It will work without any errors or warnings.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;TEST1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Call the example procedure with a value that doesn&amp;rsquo;t fit into the column. It will still work, but it will write to the error handling table, indicating a warning and that the data was truncated.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CALL exampleProc(&amp;quot;Test1 Test1 Test1 Test1 Test1&amp;quot;);&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="advanced_error_handling"&gt;Advanced Error Handling&lt;/h2&gt;
&lt;p&gt;Some queries may have more than one condition for an exception or warning. If you want all of them to be written, you will need to loop through them and insert them individually. You may want to apply the same approach to any other handlers you have. Please ensure you have read through the documentation about continue and exit handlers, as there are precedence rules for these handlers.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;– Example to get number of conditions 
GET DIAGNOSTICS @num_conditions = NUMBER;

– Example loop through conditions
FOR i IN 1..@num_conditions
   DO
      GET DIAGNOSTICS CONDITION i @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
      SET @full_error = CONCAT(&amp;quot;ERROR &amp;quot;, @errno, &amp;quot; (&amp;quot;, @sqlstate, &amp;quot;): &amp;quot;, @text);
      INSERT INTO errorhandling  VALUES (@proc_name, @full_error, current_timestamp);
END FOR;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/5</link><pubDate>Thu, 22 Feb 2024 17:32:43 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 5 posted to Guide by Appian Max Team on 2/22/2024 5:32:43 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE test (name VARCHAR(10));&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="sqlexception_handling"&gt;SQLException handling&lt;/h2&gt;
&lt;p&gt;First, create an error log table to store errors:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;CREATE TABLE errorhandling (
  `id`             INT AUTO_INCREMENT,
  `procedureName`  VARCHAR(255),
  `errorText`      VARCHAR(5000),
  `errorNumber`    SMALLINT UNSIGNED,
  `sqlState`       VARCHAR(5),
  `created`        TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, add the following SQL statements after the &amp;ldquo;BEGIN&amp;rdquo; line to capture SQL Exceptions:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;# Exception handling
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
  GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
  @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
  # set procedure name variable for logging
  SET @proc_name = ‘exampleProc’;
  SET @error_text = CONCAT(“ERROR: “, @text);
  INSERT INTO errorhandling(`procedureName`, `errorText`, `errorNumber`, `sqlState`) VALUES (@proc_name, @error_text, @errno, @sqlstate);
END;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="drop_table_/_view_/_stored_procedure_/_functions_/_triggers"&gt;DROP TABLE / View / Stored Procedure / Functions / Triggers&lt;/h2&gt;
&lt;p&gt;Using &amp;ldquo;IF EXISTS&amp;rdquo; when dropping a table or a view from will drop the table/view/stored procedure only if it exists. If the table/view/stored procedure has already been dropped, no error message will be thrown.&lt;/p&gt;
&lt;p&gt;For&amp;nbsp;example, the following code for dropping table,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE IF EXISTS `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Similarly, the SQL for dropping a view,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW IF EXISTS `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;For a stored procedure,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE `yourprocedure`
;  
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Finally, for triggers and functions you can write&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP FUNCTION IF EXISTS `yourfunction`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create_stored_procedure/function/trigger"&gt;CREATE Stored Procedure/Function/Trigger&lt;/h2&gt;
&lt;p&gt;Creating a stored procedure works a little differently than creating a table. You will first have to drop a stored procedure if it exists and then create another one with the same name. You would take the same approach to alter a stored procedure as well.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE PROCEDURE
    `yourprocedure`...
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;

CREATE PROCEDURE
    `yourprocedure`...&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The same approach works for creating and modifying functions and triggers. You can add a DROP IF EXISTS in front of the code which creates the function/trigger:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
DROP FUNCTION IF EXISTS `yourfunction`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="alter_table"&gt;ALTER TABLE&lt;/h2&gt;
&lt;p&gt;There is no single approach to creating a rerunnable ALTER TABLE script. Some ALTER TABLE scripts are always rerunnable but in other cases&amp;nbsp;you have to use a stored procedure to make the query rerunnable&amp;nbsp;(as shown in the example below).&lt;/p&gt;
&lt;p&gt;For example, the following line modifies the datatype of a column and will always be possible to rerun:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;ALTER TABLE `yourtable` MODIFY `column1` VARCHAR(255)
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;However for adding a column the situation is more difficult. You can use &amp;ldquo;IF NOT EXISTS&amp;rdquo; inside a temporary stored procedure to make the script possible to rerun.&lt;/p&gt;
&lt;p&gt;Below are the high level steps for running a rerunnable ALTER statement:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a stored procedure&lt;/li&gt;
&lt;li&gt;Check if modification already exists using INFORMATION_SCHEMA&lt;/li&gt;
&lt;li&gt;Execute the alter statement conditionally (IF, THEN, ELSE)&lt;/li&gt;
&lt;li&gt;Close the stored procedure&lt;/li&gt;
&lt;li&gt;Execute the stored procedure&lt;/li&gt;
&lt;li&gt;Drop the stored procedure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We also use all the rerunning techniques we have already learned when carrying out each step. For example, we drop the procedure if it exists before creating it.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_alter_table
;

DELIMITER $$
CREATE PROCEDURE
    sp_alter_table()
BEGIN
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.columns
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;column3&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE yourtable ADD `column3` VARCHAR(255)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_alter_table();
DROP PROCEDURE sp_alter_table
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Note: Using &amp;ldquo;IF NOT EXISTS&amp;rdquo; on the INFORMATION_SCHEMA.COLUMNS table will check to see if the column exists before adding the column.&lt;/p&gt;
&lt;p&gt;You may be wondering about how you could use this to drop columns. A similar method would work, but&amp;nbsp;Appian recommends that you do NOT drop columns from a table to avoid any issues that may arise due to backward compatibility.&lt;/p&gt;
&lt;p&gt;Adding or removing constraints follows the same high level steps as adding a column, as&amp;nbsp;explained above. However, you should check for the constraint by querying INFORMATION_SCHEMA.STATISTICS instead of INFORMATION_SCHEMA.COLUMNS. See&amp;nbsp;the&amp;nbsp;example below:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_addconstraint
;

DELIMITER $$
CREATE PROCEDURE
    sp_addconstraint()
begin
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.statistics
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;id&amp;#39;
            AND index_name   = &amp;#39;PRIMARY&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE `yourtable` ADD PRIMARY KEY (id)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_addconstraint();
DROP PROCEDURE sp_addconstraint
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;with_primary_keys"&gt;INSERT INTO&amp;nbsp;with primary keys&lt;/h2&gt;
&lt;p&gt;For inserts, if you are inserting values where you are providing the primary key (or unique identifiers), you can use the IGNORE statement to make the insert rerunnable.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT IGNORE
INTO
    `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Assuming that `id` is the primary key, if a value with `1` or `2` in `id` already exists, this statement will not insert this row of data in the table.&amp;nbsp;However, if you want to update the row to the values in the query, the statement can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;    ,
        &amp;#39;A2&amp;#39;,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;    ,
        &amp;#39;D2&amp;#39;,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
ON
    DUPLICATE KEY
UPDATE
    column1 = VALUES
    (column1
    )
    ,
    column2 = VALUES
    (column2
    )
    ,
    column3 = VALUES
    (column3
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;without_primary_keys:"&gt;INSERT INTO&amp;nbsp;without primary keys:&lt;/h2&gt;
&lt;p&gt;If you are not providing any primary keys with the values you wish to insert, you will have to use another method for inserting while avoiding duplicate inserts on reruns. By using a temporary table, we can define what values already exist in the target table before inserting them (use the LIKE argument to construct a mirror of the target table). Each row that already exists in the target table can be flagged as duplicate and avoided when inserting. Below is an example of using a temporary table to insert values in a way that can be rerun.&lt;/p&gt;
&lt;p&gt;High Level Steps&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create temporary table with extra column for flagging duplicates&lt;/li&gt;
&lt;li&gt;Insert values into temporary table&lt;/li&gt;
&lt;li&gt;Update the temporary table: flag any row that already exists in the target table&lt;/li&gt;
&lt;li&gt;Insert into target from temporary table where not flagged as existing&lt;/li&gt;
&lt;li&gt;Drop the temp table&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Code to achieve this is included below - if you have been following along, the table `yourtable` will need to have auto increment added to the `id` column for this script to run (in addition to the other changes from the ALTER TABLE section). This is a longer example, so we will break it down in a moment. Note that temporary tables only exist for the duration of the transaction, so this code cannot be run a bit at a time, you must run the whole thing in one go.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;-- create temporary table with extra column to flag duplicates
CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;

-- insert values into temp table
INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;

-- join on compound keys and update matches as existing
UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;

-- insert only those that do not exist already
INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Firstly, we are creating a temporary table, which has an extra column in it called &amp;lsquo;duplicate` which we use to mark out duplicate rows. The additional column is a tinyint(1) which is what we use in Appian for booleans, and defaults to 0 (false). We will later set duplicate rows to 1 (true).&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, we insert the rows we want to add into the temporary table first. Like so:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;In the next stage, we look for duplicates by comparing the temporary table with our actual table. For now, a duplicate is any row where all columns contain the same value except for the primary key.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Now we have a temporary table containing all the data we wish to insert and we know which rows are duplicates because they have a 1 in the duplicate column. All that remains is copy the non-duplicate rows into our actual table - and of course to drop the temporary table in case another developer needs one like yours when this change goes to your production environment.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;What is a duplicated row? It is tempting to say any row in the table which contains the same data in every column apart from the primary key is a duplicate. However, the situation may not be as simple as this. Consider a table of books. Suppose we store the author, title and the artist who designed the cover for the current edition. In this case, we likely consider a row to be a duplicate if the author and title are the same. If we have different artists in data we want to insert, we will want to update the artist.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;To handle this type of situation, we modify step 3 to do an update of existing values in the target table as well as mark rows as duplicate. We will mark the rows as duplicate if the appropriate columns contain the same value. In the book example, this will only be the author and title. If we find a match here we would update the cover artist.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For our abstract table, we will suppose rows are the same if columns 1 and 3 are the same, and update column 2 if a match is found. So the code for the third step becomes:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1,
            your.column2     = tmp.column2
;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/4</link><pubDate>Thu, 22 Feb 2024 17:29:17 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 4 posted to Guide by Appian Max Team on 2/22/2024 5:29:17 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TABLE test (name VARCHAR(10));&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="sql"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create/replace_view"&gt;CREATE/REPLACE VIEW&lt;/h2&gt;
&lt;p&gt;A rerunnable view creation script can be written using &amp;ldquo;CREATE OR REPLACE VIEW&amp;rdquo; instead of &amp;ldquo;CREATE VIEW&amp;rdquo;&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE VIEW `yourview` AS
SELECT
    `column1`
FROM
    `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE OR REPLACE VIEW `yourview` AS
SELECT
    `column1`
FROM
    `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="drop_table_/_view_/_stored_procedure_/_functions_/_triggers"&gt;DROP TABLE / View / Stored Procedure / Functions / Triggers&lt;/h2&gt;
&lt;p&gt;Using &amp;ldquo;IF EXISTS&amp;rdquo; when dropping a table or a view from will drop the table/view/stored procedure only if it exists. If the table/view/stored procedure has already been dropped, no error message will be thrown.&lt;/p&gt;
&lt;p&gt;For&amp;nbsp;example, the following code for dropping table,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE IF EXISTS `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Similarly, the SQL for dropping a view,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW IF EXISTS `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;For a stored procedure,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE `yourprocedure`
;  
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Finally, for triggers and functions you can write&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP FUNCTION IF EXISTS `yourfunction`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create_stored_procedure/function/trigger"&gt;CREATE Stored Procedure/Function/Trigger&lt;/h2&gt;
&lt;p&gt;Creating a stored procedure works a little differently than creating a table. You will first have to drop a stored procedure if it exists and then create another one with the same name. You would take the same approach to alter a stored procedure as well.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE PROCEDURE
    `yourprocedure`...
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;

CREATE PROCEDURE
    `yourprocedure`...&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The same approach works for creating and modifying functions and triggers. You can add a DROP IF EXISTS in front of the code which creates the function/trigger:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
DROP FUNCTION IF EXISTS `yourfunction`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="alter_table"&gt;ALTER TABLE&lt;/h2&gt;
&lt;p&gt;There is no single approach to creating a rerunnable ALTER TABLE script. Some ALTER TABLE scripts are always rerunnable but in other cases&amp;nbsp;you have to use a stored procedure to make the query rerunnable&amp;nbsp;(as shown in the example below).&lt;/p&gt;
&lt;p&gt;For example, the following line modifies the datatype of a column and will always be possible to rerun:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;ALTER TABLE `yourtable` MODIFY `column1` VARCHAR(255)
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;However for adding a column the situation is more difficult. You can use &amp;ldquo;IF NOT EXISTS&amp;rdquo; inside a temporary stored procedure to make the script possible to rerun.&lt;/p&gt;
&lt;p&gt;Below are the high level steps for running a rerunnable ALTER statement:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a stored procedure&lt;/li&gt;
&lt;li&gt;Check if modification already exists using INFORMATION_SCHEMA&lt;/li&gt;
&lt;li&gt;Execute the alter statement conditionally (IF, THEN, ELSE)&lt;/li&gt;
&lt;li&gt;Close the stored procedure&lt;/li&gt;
&lt;li&gt;Execute the stored procedure&lt;/li&gt;
&lt;li&gt;Drop the stored procedure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We also use all the rerunning techniques we have already learned when carrying out each step. For example, we drop the procedure if it exists before creating it.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_alter_table
;

DELIMITER $$
CREATE PROCEDURE
    sp_alter_table()
BEGIN
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.columns
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;column3&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE yourtable ADD `column3` VARCHAR(255)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_alter_table();
DROP PROCEDURE sp_alter_table
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Note: Using &amp;ldquo;IF NOT EXISTS&amp;rdquo; on the INFORMATION_SCHEMA.COLUMNS table will check to see if the column exists before adding the column.&lt;/p&gt;
&lt;p&gt;You may be wondering about how you could use this to drop columns. A similar method would work, but&amp;nbsp;Appian recommends that you do NOT drop columns from a table to avoid any issues that may arise due to backward compatibility.&lt;/p&gt;
&lt;p&gt;Adding or removing constraints follows the same high level steps as adding a column, as&amp;nbsp;explained above. However, you should check for the constraint by querying INFORMATION_SCHEMA.STATISTICS instead of INFORMATION_SCHEMA.COLUMNS. See&amp;nbsp;the&amp;nbsp;example below:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_addconstraint
;

DELIMITER $$
CREATE PROCEDURE
    sp_addconstraint()
begin
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.statistics
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;id&amp;#39;
            AND index_name   = &amp;#39;PRIMARY&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE `yourtable` ADD PRIMARY KEY (id)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_addconstraint();
DROP PROCEDURE sp_addconstraint
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;with_primary_keys"&gt;INSERT INTO&amp;nbsp;with primary keys&lt;/h2&gt;
&lt;p&gt;For inserts, if you are inserting values where you are providing the primary key (or unique identifiers), you can use the IGNORE statement to make the insert rerunnable.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT IGNORE
INTO
    `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Assuming that `id` is the primary key, if a value with `1` or `2` in `id` already exists, this statement will not insert this row of data in the table.&amp;nbsp;However, if you want to update the row to the values in the query, the statement can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;    ,
        &amp;#39;A2&amp;#39;,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;    ,
        &amp;#39;D2&amp;#39;,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
ON
    DUPLICATE KEY
UPDATE
    column1 = VALUES
    (column1
    )
    ,
    column2 = VALUES
    (column2
    )
    ,
    column3 = VALUES
    (column3
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;without_primary_keys:"&gt;INSERT INTO&amp;nbsp;without primary keys:&lt;/h2&gt;
&lt;p&gt;If you are not providing any primary keys with the values you wish to insert, you will have to use another method for inserting while avoiding duplicate inserts on reruns. By using a temporary table, we can define what values already exist in the target table before inserting them (use the LIKE argument to construct a mirror of the target table). Each row that already exists in the target table can be flagged as duplicate and avoided when inserting. Below is an example of using a temporary table to insert values in a way that can be rerun.&lt;/p&gt;
&lt;p&gt;High Level Steps&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create temporary table with extra column for flagging duplicates&lt;/li&gt;
&lt;li&gt;Insert values into temporary table&lt;/li&gt;
&lt;li&gt;Update the temporary table: flag any row that already exists in the target table&lt;/li&gt;
&lt;li&gt;Insert into target from temporary table where not flagged as existing&lt;/li&gt;
&lt;li&gt;Drop the temp table&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Code to achieve this is included below - if you have been following along, the table `yourtable` will need to have auto increment added to the `id` column for this script to run (in addition to the other changes from the ALTER TABLE section). This is a longer example, so we will break it down in a moment. Note that temporary tables only exist for the duration of the transaction, so this code cannot be run a bit at a time, you must run the whole thing in one go.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;-- create temporary table with extra column to flag duplicates
CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;

-- insert values into temp table
INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;

-- join on compound keys and update matches as existing
UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;

-- insert only those that do not exist already
INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Firstly, we are creating a temporary table, which has an extra column in it called &amp;lsquo;duplicate` which we use to mark out duplicate rows. The additional column is a tinyint(1) which is what we use in Appian for booleans, and defaults to 0 (false). We will later set duplicate rows to 1 (true).&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, we insert the rows we want to add into the temporary table first. Like so:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;In the next stage, we look for duplicates by comparing the temporary table with our actual table. For now, a duplicate is any row where all columns contain the same value except for the primary key.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Now we have a temporary table containing all the data we wish to insert and we know which rows are duplicates because they have a 1 in the duplicate column. All that remains is copy the non-duplicate rows into our actual table - and of course to drop the temporary table in case another developer needs one like yours when this change goes to your production environment.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;What is a duplicated row? It is tempting to say any row in the table which contains the same data in every column apart from the primary key is a duplicate. However, the situation may not be as simple as this. Consider a table of books. Suppose we store the author, title and the artist who designed the cover for the current edition. In this case, we likely consider a row to be a duplicate if the author and title are the same. If we have different artists in data we want to insert, we will want to update the artist.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;To handle this type of situation, we modify step 3 to do an update of existing values in the target table as well as mark rows as duplicate. We will mark the rows as duplicate if the appropriate columns contain the same value. In the book example, this will only be the author and title. If we find a match here we would update the cover artist.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For our abstract table, we will suppose rows are the same if columns 1 and 3 are the same, and update column 2 if a match is found. So the code for the third step becomes:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1,
            your.column2     = tmp.column2
;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/3</link><pubDate>Thu, 22 Feb 2024 17:27:49 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 3 posted to Guide by Appian Max Team on 2/22/2024 5:27:49 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TABLE test (name VARCHAR(10));&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Create a simple stored procedure:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DELIMITER //

CREATE OR REPLACE PROCEDURE exampleProc(IN p_text VARCHAR(255))
BEGIN
  # Procedure body
  INSERT INTO test SELECT p_text;
END;
//

DELIMITER ;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create/replace_view"&gt;CREATE/REPLACE VIEW&lt;/h2&gt;
&lt;p&gt;A rerunnable view creation script can be written using &amp;ldquo;CREATE OR REPLACE VIEW&amp;rdquo; instead of &amp;ldquo;CREATE VIEW&amp;rdquo;&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE VIEW `yourview` AS
SELECT
    `column1`
FROM
    `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE OR REPLACE VIEW `yourview` AS
SELECT
    `column1`
FROM
    `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="drop_table_/_view_/_stored_procedure_/_functions_/_triggers"&gt;DROP TABLE / View / Stored Procedure / Functions / Triggers&lt;/h2&gt;
&lt;p&gt;Using &amp;ldquo;IF EXISTS&amp;rdquo; when dropping a table or a view from will drop the table/view/stored procedure only if it exists. If the table/view/stored procedure has already been dropped, no error message will be thrown.&lt;/p&gt;
&lt;p&gt;For&amp;nbsp;example, the following code for dropping table,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE IF EXISTS `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Similarly, the SQL for dropping a view,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW IF EXISTS `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;For a stored procedure,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE `yourprocedure`
;  
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Finally, for triggers and functions you can write&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP FUNCTION IF EXISTS `yourfunction`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create_stored_procedure/function/trigger"&gt;CREATE Stored Procedure/Function/Trigger&lt;/h2&gt;
&lt;p&gt;Creating a stored procedure works a little differently than creating a table. You will first have to drop a stored procedure if it exists and then create another one with the same name. You would take the same approach to alter a stored procedure as well.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE PROCEDURE
    `yourprocedure`...
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;

CREATE PROCEDURE
    `yourprocedure`...&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The same approach works for creating and modifying functions and triggers. You can add a DROP IF EXISTS in front of the code which creates the function/trigger:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
DROP FUNCTION IF EXISTS `yourfunction`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="alter_table"&gt;ALTER TABLE&lt;/h2&gt;
&lt;p&gt;There is no single approach to creating a rerunnable ALTER TABLE script. Some ALTER TABLE scripts are always rerunnable but in other cases&amp;nbsp;you have to use a stored procedure to make the query rerunnable&amp;nbsp;(as shown in the example below).&lt;/p&gt;
&lt;p&gt;For example, the following line modifies the datatype of a column and will always be possible to rerun:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;ALTER TABLE `yourtable` MODIFY `column1` VARCHAR(255)
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;However for adding a column the situation is more difficult. You can use &amp;ldquo;IF NOT EXISTS&amp;rdquo; inside a temporary stored procedure to make the script possible to rerun.&lt;/p&gt;
&lt;p&gt;Below are the high level steps for running a rerunnable ALTER statement:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a stored procedure&lt;/li&gt;
&lt;li&gt;Check if modification already exists using INFORMATION_SCHEMA&lt;/li&gt;
&lt;li&gt;Execute the alter statement conditionally (IF, THEN, ELSE)&lt;/li&gt;
&lt;li&gt;Close the stored procedure&lt;/li&gt;
&lt;li&gt;Execute the stored procedure&lt;/li&gt;
&lt;li&gt;Drop the stored procedure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We also use all the rerunning techniques we have already learned when carrying out each step. For example, we drop the procedure if it exists before creating it.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_alter_table
;

DELIMITER $$
CREATE PROCEDURE
    sp_alter_table()
BEGIN
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.columns
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;column3&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE yourtable ADD `column3` VARCHAR(255)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_alter_table();
DROP PROCEDURE sp_alter_table
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Note: Using &amp;ldquo;IF NOT EXISTS&amp;rdquo; on the INFORMATION_SCHEMA.COLUMNS table will check to see if the column exists before adding the column.&lt;/p&gt;
&lt;p&gt;You may be wondering about how you could use this to drop columns. A similar method would work, but&amp;nbsp;Appian recommends that you do NOT drop columns from a table to avoid any issues that may arise due to backward compatibility.&lt;/p&gt;
&lt;p&gt;Adding or removing constraints follows the same high level steps as adding a column, as&amp;nbsp;explained above. However, you should check for the constraint by querying INFORMATION_SCHEMA.STATISTICS instead of INFORMATION_SCHEMA.COLUMNS. See&amp;nbsp;the&amp;nbsp;example below:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_addconstraint
;

DELIMITER $$
CREATE PROCEDURE
    sp_addconstraint()
begin
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.statistics
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;id&amp;#39;
            AND index_name   = &amp;#39;PRIMARY&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE `yourtable` ADD PRIMARY KEY (id)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_addconstraint();
DROP PROCEDURE sp_addconstraint
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;with_primary_keys"&gt;INSERT INTO&amp;nbsp;with primary keys&lt;/h2&gt;
&lt;p&gt;For inserts, if you are inserting values where you are providing the primary key (or unique identifiers), you can use the IGNORE statement to make the insert rerunnable.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT IGNORE
INTO
    `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Assuming that `id` is the primary key, if a value with `1` or `2` in `id` already exists, this statement will not insert this row of data in the table.&amp;nbsp;However, if you want to update the row to the values in the query, the statement can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;    ,
        &amp;#39;A2&amp;#39;,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;    ,
        &amp;#39;D2&amp;#39;,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
ON
    DUPLICATE KEY
UPDATE
    column1 = VALUES
    (column1
    )
    ,
    column2 = VALUES
    (column2
    )
    ,
    column3 = VALUES
    (column3
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;without_primary_keys:"&gt;INSERT INTO&amp;nbsp;without primary keys:&lt;/h2&gt;
&lt;p&gt;If you are not providing any primary keys with the values you wish to insert, you will have to use another method for inserting while avoiding duplicate inserts on reruns. By using a temporary table, we can define what values already exist in the target table before inserting them (use the LIKE argument to construct a mirror of the target table). Each row that already exists in the target table can be flagged as duplicate and avoided when inserting. Below is an example of using a temporary table to insert values in a way that can be rerun.&lt;/p&gt;
&lt;p&gt;High Level Steps&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create temporary table with extra column for flagging duplicates&lt;/li&gt;
&lt;li&gt;Insert values into temporary table&lt;/li&gt;
&lt;li&gt;Update the temporary table: flag any row that already exists in the target table&lt;/li&gt;
&lt;li&gt;Insert into target from temporary table where not flagged as existing&lt;/li&gt;
&lt;li&gt;Drop the temp table&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Code to achieve this is included below - if you have been following along, the table `yourtable` will need to have auto increment added to the `id` column for this script to run (in addition to the other changes from the ALTER TABLE section). This is a longer example, so we will break it down in a moment. Note that temporary tables only exist for the duration of the transaction, so this code cannot be run a bit at a time, you must run the whole thing in one go.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;-- create temporary table with extra column to flag duplicates
CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;

-- insert values into temp table
INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;

-- join on compound keys and update matches as existing
UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;

-- insert only those that do not exist already
INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Firstly, we are creating a temporary table, which has an extra column in it called &amp;lsquo;duplicate` which we use to mark out duplicate rows. The additional column is a tinyint(1) which is what we use in Appian for booleans, and defaults to 0 (false). We will later set duplicate rows to 1 (true).&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, we insert the rows we want to add into the temporary table first. Like so:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;In the next stage, we look for duplicates by comparing the temporary table with our actual table. For now, a duplicate is any row where all columns contain the same value except for the primary key.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Now we have a temporary table containing all the data we wish to insert and we know which rows are duplicates because they have a 1 in the duplicate column. All that remains is copy the non-duplicate rows into our actual table - and of course to drop the temporary table in case another developer needs one like yours when this change goes to your production environment.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;What is a duplicated row? It is tempting to say any row in the table which contains the same data in every column apart from the primary key is a duplicate. However, the situation may not be as simple as this. Consider a table of books. Suppose we store the author, title and the artist who designed the cover for the current edition. In this case, we likely consider a row to be a duplicate if the author and title are the same. If we have different artists in data we want to insert, we will want to update the artist.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;To handle this type of situation, we modify step 3 to do an update of existing values in the target table as well as mark rows as duplicate. We will mark the rows as duplicate if the appropriate columns contain the same value. In the book example, this will only be the author and title. If we find a match here we would update the cover artist.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For our abstract table, we will suppose rows are the same if columns 1 and 3 are the same, and update column 2 if a match is found. So the code for the third step becomes:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1,
            your.column2     = tmp.column2
;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/2</link><pubDate>Thu, 22 Feb 2024 17:25:54 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 2 posted to Guide by Appian Max Team on 2/22/2024 5:25:54 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide will walk you through adding simple error handling to a stored procedure created in the Appian cloud database (MariaDB), allowing you to capture errors and warnings. Refer to the official documentation for your database if you are using one other than MariaDB, as the examples provided below may have syntax that only applies to MariaDB.&lt;/p&gt;
&lt;h2 id="example_setup"&gt;Example Setup&lt;/h2&gt;
&lt;p&gt;Our example stored procedure will be used to insert string values into a test table.&lt;/p&gt;
&lt;p&gt;Create a simple test table:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TABLE test (name VARCHAR(10));&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TABLE
    IF NOT EXISTS `yourtable`
    (
        `id`      INT NOT NULL                  ,
        `column1` VARCHAR(255) NULL DEFAULT NULL,
        `column2` VARCHAR(255) NULL DEFAULT NULL
    )
    ENGINE = InnoDB
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create/replace_view"&gt;CREATE/REPLACE VIEW&lt;/h2&gt;
&lt;p&gt;A rerunnable view creation script can be written using &amp;ldquo;CREATE OR REPLACE VIEW&amp;rdquo; instead of &amp;ldquo;CREATE VIEW&amp;rdquo;&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE VIEW `yourview` AS
SELECT
    `column1`
FROM
    `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE OR REPLACE VIEW `yourview` AS
SELECT
    `column1`
FROM
    `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="drop_table_/_view_/_stored_procedure_/_functions_/_triggers"&gt;DROP TABLE / View / Stored Procedure / Functions / Triggers&lt;/h2&gt;
&lt;p&gt;Using &amp;ldquo;IF EXISTS&amp;rdquo; when dropping a table or a view from will drop the table/view/stored procedure only if it exists. If the table/view/stored procedure has already been dropped, no error message will be thrown.&lt;/p&gt;
&lt;p&gt;For&amp;nbsp;example, the following code for dropping table,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE IF EXISTS `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Similarly, the SQL for dropping a view,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW IF EXISTS `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;For a stored procedure,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE `yourprocedure`
;  
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Finally, for triggers and functions you can write&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP FUNCTION IF EXISTS `yourfunction`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create_stored_procedure/function/trigger"&gt;CREATE Stored Procedure/Function/Trigger&lt;/h2&gt;
&lt;p&gt;Creating a stored procedure works a little differently than creating a table. You will first have to drop a stored procedure if it exists and then create another one with the same name. You would take the same approach to alter a stored procedure as well.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE PROCEDURE
    `yourprocedure`...
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;

CREATE PROCEDURE
    `yourprocedure`...&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The same approach works for creating and modifying functions and triggers. You can add a DROP IF EXISTS in front of the code which creates the function/trigger:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
DROP FUNCTION IF EXISTS `yourfunction`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="alter_table"&gt;ALTER TABLE&lt;/h2&gt;
&lt;p&gt;There is no single approach to creating a rerunnable ALTER TABLE script. Some ALTER TABLE scripts are always rerunnable but in other cases&amp;nbsp;you have to use a stored procedure to make the query rerunnable&amp;nbsp;(as shown in the example below).&lt;/p&gt;
&lt;p&gt;For example, the following line modifies the datatype of a column and will always be possible to rerun:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;ALTER TABLE `yourtable` MODIFY `column1` VARCHAR(255)
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;However for adding a column the situation is more difficult. You can use &amp;ldquo;IF NOT EXISTS&amp;rdquo; inside a temporary stored procedure to make the script possible to rerun.&lt;/p&gt;
&lt;p&gt;Below are the high level steps for running a rerunnable ALTER statement:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a stored procedure&lt;/li&gt;
&lt;li&gt;Check if modification already exists using INFORMATION_SCHEMA&lt;/li&gt;
&lt;li&gt;Execute the alter statement conditionally (IF, THEN, ELSE)&lt;/li&gt;
&lt;li&gt;Close the stored procedure&lt;/li&gt;
&lt;li&gt;Execute the stored procedure&lt;/li&gt;
&lt;li&gt;Drop the stored procedure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We also use all the rerunning techniques we have already learned when carrying out each step. For example, we drop the procedure if it exists before creating it.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_alter_table
;

DELIMITER $$
CREATE PROCEDURE
    sp_alter_table()
BEGIN
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.columns
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;column3&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE yourtable ADD `column3` VARCHAR(255)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_alter_table();
DROP PROCEDURE sp_alter_table
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Note: Using &amp;ldquo;IF NOT EXISTS&amp;rdquo; on the INFORMATION_SCHEMA.COLUMNS table will check to see if the column exists before adding the column.&lt;/p&gt;
&lt;p&gt;You may be wondering about how you could use this to drop columns. A similar method would work, but&amp;nbsp;Appian recommends that you do NOT drop columns from a table to avoid any issues that may arise due to backward compatibility.&lt;/p&gt;
&lt;p&gt;Adding or removing constraints follows the same high level steps as adding a column, as&amp;nbsp;explained above. However, you should check for the constraint by querying INFORMATION_SCHEMA.STATISTICS instead of INFORMATION_SCHEMA.COLUMNS. See&amp;nbsp;the&amp;nbsp;example below:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_addconstraint
;

DELIMITER $$
CREATE PROCEDURE
    sp_addconstraint()
begin
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.statistics
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;id&amp;#39;
            AND index_name   = &amp;#39;PRIMARY&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE `yourtable` ADD PRIMARY KEY (id)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_addconstraint();
DROP PROCEDURE sp_addconstraint
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;with_primary_keys"&gt;INSERT INTO&amp;nbsp;with primary keys&lt;/h2&gt;
&lt;p&gt;For inserts, if you are inserting values where you are providing the primary key (or unique identifiers), you can use the IGNORE statement to make the insert rerunnable.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT IGNORE
INTO
    `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Assuming that `id` is the primary key, if a value with `1` or `2` in `id` already exists, this statement will not insert this row of data in the table.&amp;nbsp;However, if you want to update the row to the values in the query, the statement can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;    ,
        &amp;#39;A2&amp;#39;,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;    ,
        &amp;#39;D2&amp;#39;,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
ON
    DUPLICATE KEY
UPDATE
    column1 = VALUES
    (column1
    )
    ,
    column2 = VALUES
    (column2
    )
    ,
    column3 = VALUES
    (column3
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;without_primary_keys:"&gt;INSERT INTO&amp;nbsp;without primary keys:&lt;/h2&gt;
&lt;p&gt;If you are not providing any primary keys with the values you wish to insert, you will have to use another method for inserting while avoiding duplicate inserts on reruns. By using a temporary table, we can define what values already exist in the target table before inserting them (use the LIKE argument to construct a mirror of the target table). Each row that already exists in the target table can be flagged as duplicate and avoided when inserting. Below is an example of using a temporary table to insert values in a way that can be rerun.&lt;/p&gt;
&lt;p&gt;High Level Steps&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create temporary table with extra column for flagging duplicates&lt;/li&gt;
&lt;li&gt;Insert values into temporary table&lt;/li&gt;
&lt;li&gt;Update the temporary table: flag any row that already exists in the target table&lt;/li&gt;
&lt;li&gt;Insert into target from temporary table where not flagged as existing&lt;/li&gt;
&lt;li&gt;Drop the temp table&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Code to achieve this is included below - if you have been following along, the table `yourtable` will need to have auto increment added to the `id` column for this script to run (in addition to the other changes from the ALTER TABLE section). This is a longer example, so we will break it down in a moment. Note that temporary tables only exist for the duration of the transaction, so this code cannot be run a bit at a time, you must run the whole thing in one go.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;-- create temporary table with extra column to flag duplicates
CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;

-- insert values into temp table
INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;

-- join on compound keys and update matches as existing
UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;

-- insert only those that do not exist already
INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Firstly, we are creating a temporary table, which has an extra column in it called &amp;lsquo;duplicate` which we use to mark out duplicate rows. The additional column is a tinyint(1) which is what we use in Appian for booleans, and defaults to 0 (false). We will later set duplicate rows to 1 (true).&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, we insert the rows we want to add into the temporary table first. Like so:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;In the next stage, we look for duplicates by comparing the temporary table with our actual table. For now, a duplicate is any row where all columns contain the same value except for the primary key.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Now we have a temporary table containing all the data we wish to insert and we know which rows are duplicates because they have a 1 in the duplicate column. All that remains is copy the non-duplicate rows into our actual table - and of course to drop the temporary table in case another developer needs one like yours when this change goes to your production environment.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;What is a duplicated row? It is tempting to say any row in the table which contains the same data in every column apart from the primary key is a duplicate. However, the situation may not be as simple as this. Consider a table of books. Suppose we store the author, title and the artist who designed the cover for the current edition. In this case, we likely consider a row to be a duplicate if the author and title are the same. If we have different artists in data we want to insert, we will want to update the artist.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;To handle this type of situation, we modify step 3 to do an update of existing values in the target table as well as mark rows as duplicate. We will mark the rows as duplicate if the appropriate columns contain the same value. In the book example, this will only be the author and title. If we find a match here we would update the cover artist.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For our abstract table, we will suppose rows are the same if columns 1 and 3 are the same, and update column 2 if a match is found. So the code for the third step becomes:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1,
            your.column2     = tmp.column2
;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Add Error Handling to Stored Procedures</title><link>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures/revision/1</link><pubDate>Thu, 22 Feb 2024 17:21:42 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:42910279-6c3a-4c41-af13-bd784f6e47cf</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3491/add-error-handling-to-stored-procedures#comments</comments><description>Revision 1 posted to Guide by Appian Max Team on 2/22/2024 5:21:42 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;p&gt;This guide deals with writing database scripts&amp;nbsp;which can be rerun (&amp;lsquo;rerunnable&amp;rsquo;). Creating rerunnable scripts makes deployments easier and avoids any unwanted behavior if the code were to run more than once. When modifying or updating a database, running a normal script once will work as expected without errors. However, if the script were to run again, the database may return an error message, halt execution or even worse, create unwanted duplicates. This could drastically slow down the deployment process.&lt;/p&gt;
&lt;p&gt;This play will show you how to add simple checks to your database scripts so that they can be run more than once without errors.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The types of statements that can have such checks to ensure they are rerunnable are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CREATE TABLE&lt;/li&gt;
&lt;li&gt;CREATE/REPLACE View&lt;/li&gt;
&lt;li&gt;DROP TABLE/DROP VIEW/DROP Stored Procedure/Function/Trigger&lt;/li&gt;
&lt;li&gt;CREATE Stored Procedure/Function/Trigger&lt;/li&gt;
&lt;li&gt;ALTER TABLE&lt;/li&gt;
&lt;li&gt;INSERT INTO&amp;nbsp;with Primary Keys&lt;/li&gt;
&lt;li&gt;INSERT INTO&amp;nbsp;without Primary Keys&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most of these steps should be taken on every script and every project because they are such simple steps. However, &amp;lsquo;ALTER TABLE&amp;rsquo; (5) and &amp;lsquo;INSERT INTO&amp;rsquo; without Primary Keys (7) require significant additional work. As such, they are more appropriate for large projects where the need to make deployments as easy as possible outweighs the additional development effort.&lt;/p&gt;
&lt;h2 id="create_table"&gt;CREATE TABLE&lt;/h2&gt;
&lt;p&gt;When creating a table, using &amp;ldquo;IF NOT EXISTS&amp;rdquo; in the script will check to see if the table you are trying to create already exists before executing the create statement.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TABLE `yourtable`
    (
        `id`      INT NOT NULL                  ,
        `column1` VARCHAR(255) NULL DEFAULT NULL,
        `column2` VARCHAR(255) NULL DEFAULT NULL
    )
    ENGINE = InnoDB
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TABLE
    IF NOT EXISTS `yourtable`
    (
        `id`      INT NOT NULL                  ,
        `column1` VARCHAR(255) NULL DEFAULT NULL,
        `column2` VARCHAR(255) NULL DEFAULT NULL
    )
    ENGINE = InnoDB
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create/replace_view"&gt;CREATE/REPLACE VIEW&lt;/h2&gt;
&lt;p&gt;A rerunnable view creation script can be written using &amp;ldquo;CREATE OR REPLACE VIEW&amp;rdquo; instead of &amp;ldquo;CREATE VIEW&amp;rdquo;&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE VIEW `yourview` AS
SELECT
    `column1`
FROM
    `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE OR REPLACE VIEW `yourview` AS
SELECT
    `column1`
FROM
    `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="drop_table_/_view_/_stored_procedure_/_functions_/_triggers"&gt;DROP TABLE / View / Stored Procedure / Functions / Triggers&lt;/h2&gt;
&lt;p&gt;Using &amp;ldquo;IF EXISTS&amp;rdquo; when dropping a table or a view from will drop the table/view/stored procedure only if it exists. If the table/view/stored procedure has already been dropped, no error message will be thrown.&lt;/p&gt;
&lt;p&gt;For&amp;nbsp;example, the following code for dropping table,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE `yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TABLE IF EXISTS `yourtable`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Similarly, the SQL for dropping a view,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP VIEW IF EXISTS `yourview`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;For a stored procedure,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE `yourprocedure`
;  
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Finally, for triggers and functions you can write&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP FUNCTION IF EXISTS `yourfunction`
;
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="create_stored_procedure/function/trigger"&gt;CREATE Stored Procedure/Function/Trigger&lt;/h2&gt;
&lt;p&gt;Creating a stored procedure works a little differently than creating a table. You will first have to drop a stored procedure if it exists and then create another one with the same name. You would take the same approach to alter a stored procedure as well.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE PROCEDURE
    `yourprocedure`...
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS `yourprocedure`
;

CREATE PROCEDURE
    `yourprocedure`...&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The same approach works for creating and modifying functions and triggers. You can add a DROP IF EXISTS in front of the code which creates the function/trigger:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP TRIGGER IF EXISTS `yourtrigger`
;
DROP FUNCTION IF EXISTS `yourfunction`
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="alter_table"&gt;ALTER TABLE&lt;/h2&gt;
&lt;p&gt;There is no single approach to creating a rerunnable ALTER TABLE script. Some ALTER TABLE scripts are always rerunnable but in other cases&amp;nbsp;you have to use a stored procedure to make the query rerunnable&amp;nbsp;(as shown in the example below).&lt;/p&gt;
&lt;p&gt;For example, the following line modifies the datatype of a column and will always be possible to rerun:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;ALTER TABLE `yourtable` MODIFY `column1` VARCHAR(255)
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;However for adding a column the situation is more difficult. You can use &amp;ldquo;IF NOT EXISTS&amp;rdquo; inside a temporary stored procedure to make the script possible to rerun.&lt;/p&gt;
&lt;p&gt;Below are the high level steps for running a rerunnable ALTER statement:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a stored procedure&lt;/li&gt;
&lt;li&gt;Check if modification already exists using INFORMATION_SCHEMA&lt;/li&gt;
&lt;li&gt;Execute the alter statement conditionally (IF, THEN, ELSE)&lt;/li&gt;
&lt;li&gt;Close the stored procedure&lt;/li&gt;
&lt;li&gt;Execute the stored procedure&lt;/li&gt;
&lt;li&gt;Drop the stored procedure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We also use all the rerunning techniques we have already learned when carrying out each step. For example, we drop the procedure if it exists before creating it.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_alter_table
;

DELIMITER $$
CREATE PROCEDURE
    sp_alter_table()
BEGIN
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.columns
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;column3&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE yourtable ADD `column3` VARCHAR(255)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_alter_table();
DROP PROCEDURE sp_alter_table
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Note: Using &amp;ldquo;IF NOT EXISTS&amp;rdquo; on the INFORMATION_SCHEMA.COLUMNS table will check to see if the column exists before adding the column.&lt;/p&gt;
&lt;p&gt;You may be wondering about how you could use this to drop columns. A similar method would work, but&amp;nbsp;Appian recommends that you do NOT drop columns from a table to avoid any issues that may arise due to backward compatibility.&lt;/p&gt;
&lt;p&gt;Adding or removing constraints follows the same high level steps as adding a column, as&amp;nbsp;explained above. However, you should check for the constraint by querying INFORMATION_SCHEMA.STATISTICS instead of INFORMATION_SCHEMA.COLUMNS. See&amp;nbsp;the&amp;nbsp;example below:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;DROP PROCEDURE IF EXISTS sp_addconstraint
;

DELIMITER $$
CREATE PROCEDURE
    sp_addconstraint()
begin
    DECLARE _count INT;
    SET _count =
    (
        SELECT
            count(1)
        FROM
            information_schema.statistics
        WHERE
            table_name       = &amp;#39;yourtable&amp;#39;
            AND table_schema = &amp;#39;Appian&amp;#39;
            AND column_name  = &amp;#39;id&amp;#39;
            AND index_name   = &amp;#39;PRIMARY&amp;#39;
    )
    ;
    IF _count = 0 THEN
        ALTER TABLE `yourtable` ADD PRIMARY KEY (id)
        ;
    
    END IF;
END $$
DELIMITER ;
CALL sp_addconstraint();
DROP PROCEDURE sp_addconstraint
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;with_primary_keys"&gt;INSERT INTO&amp;nbsp;with primary keys&lt;/h2&gt;
&lt;p&gt;For inserts, if you are inserting values where you are providing the primary key (or unique identifiers), you can use the IGNORE statement to make the insert rerunnable.&lt;/p&gt;
&lt;p&gt;For Example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT IGNORE
INTO
    `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;   ,
        &amp;#39;A&amp;#39;,
        &amp;#39;B&amp;#39;,
        &amp;#39;C&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;   ,
        &amp;#39;D&amp;#39;,
        &amp;#39;E&amp;#39;,
        &amp;#39;F&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Assuming that `id` is the primary key, if a value with `1` or `2` in `id` already exists, this statement will not insert this row of data in the table.&amp;nbsp;However, if you want to update the row to the values in the query, the statement can be rewritten as:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (`id`        ,
        `column1`,
        `column2`,
        `column3`
    )
    VALUES
    (&amp;#39;1&amp;#39;    ,
        &amp;#39;A2&amp;#39;,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    (&amp;#39;2&amp;#39;    ,
        &amp;#39;D2&amp;#39;,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
ON
    DUPLICATE KEY
UPDATE
    column1 = VALUES
    (column1
    )
    ,
    column2 = VALUES
    (column2
    )
    ,
    column3 = VALUES
    (column3
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="insert_into&amp;nbsp;without_primary_keys:"&gt;INSERT INTO&amp;nbsp;without primary keys:&lt;/h2&gt;
&lt;p&gt;If you are not providing any primary keys with the values you wish to insert, you will have to use another method for inserting while avoiding duplicate inserts on reruns. By using a temporary table, we can define what values already exist in the target table before inserting them (use the LIKE argument to construct a mirror of the target table). Each row that already exists in the target table can be flagged as duplicate and avoided when inserting. Below is an example of using a temporary table to insert values in a way that can be rerun.&lt;/p&gt;
&lt;p&gt;High Level Steps&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create temporary table with extra column for flagging duplicates&lt;/li&gt;
&lt;li&gt;Insert values into temporary table&lt;/li&gt;
&lt;li&gt;Update the temporary table: flag any row that already exists in the target table&lt;/li&gt;
&lt;li&gt;Insert into target from temporary table where not flagged as existing&lt;/li&gt;
&lt;li&gt;Drop the temp table&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Code to achieve this is included below - if you have been following along, the table `yourtable` will need to have auto increment added to the `id` column for this script to run (in addition to the other changes from the ALTER TABLE section). This is a longer example, so we will break it down in a moment. Note that temporary tables only exist for the duration of the transaction, so this code cannot be run a bit at a time, you must run the whole thing in one go.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;-- create temporary table with extra column to flag duplicates
CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;

-- insert values into temp table
INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;

-- join on compound keys and update matches as existing
UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;

-- insert only those that do not exist already
INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Firstly, we are creating a temporary table, which has an extra column in it called &amp;lsquo;duplicate` which we use to mark out duplicate rows. The additional column is a tinyint(1) which is what we use in Appian for booleans, and defaults to 0 (false). We will later set duplicate rows to 1 (true).&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CREATE TEMPORARY TABLE `tmp_yourtable` LIKE `yourtable`
;

ALTER TABLE `tmp_yourtable` ADD COLUMN duplicate tinyint(1) DEFAULT 0
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, we insert the rows we want to add into the temporary table first. Like so:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `tmp_yourtable`
    ( `column1`  ,
        `column2`,
        `column3`
    )
    VALUES
    ( &amp;#39;A2&amp;#39;  ,
        &amp;#39;B2&amp;#39;,
        &amp;#39;C2&amp;#39;
    )
    ,
    ( &amp;#39;D2&amp;#39;  ,
        &amp;#39;E2&amp;#39;,
        &amp;#39;F2&amp;#39;
    )
    ,
    ( &amp;#39;G&amp;#39;  ,
        &amp;#39;H&amp;#39;,
        &amp;#39;I&amp;#39;
    )
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;In the next stage, we look for duplicates by comparing the temporary table with our actual table. For now, a duplicate is any row where all columns contain the same value except for the primary key.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column2 = tmp.column2
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Now we have a temporary table containing all the data we wish to insert and we know which rows are duplicates because they have a 1 in the duplicate column. All that remains is copy the non-duplicate rows into our actual table - and of course to drop the temporary table in case another developer needs one like yours when this change goes to your production environment.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;INSERT INTO `yourtable`
    (column1   ,
        column2,
        column3
    )
SELECT
    column1,
    column2,
    column3
FROM
    `tmp_yourtable`
WHERE
    duplicate &amp;lt;&amp;gt; 1
;

DROP TEMPORARY TABLE `tmp_yourtable`
;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;What is a duplicated row? It is tempting to say any row in the table which contains the same data in every column apart from the primary key is a duplicate. However, the situation may not be as simple as this. Consider a table of books. Suppose we store the author, title and the artist who designed the cover for the current edition. In this case, we likely consider a row to be a duplicate if the author and title are the same. If we have different artists in data we want to insert, we will want to update the artist.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;To handle this type of situation, we modify step 3 to do an update of existing values in the target table as well as mark rows as duplicate. We will mark the rows as duplicate if the appropriate columns contain the same value. In the book example, this will only be the author and title. If we find a match here we would update the cover artist.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For our abstract table, we will suppose rows are the same if columns 1 and 3 are the same, and update column 2 if a match is found. So the code for the third step becomes:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;UPDATE
    `tmp_yourtable` tmp
    INNER JOIN
        `yourtable` your
        ON
            your.column1     = tmp.column1
            AND your.column3 = tmp.column3
        SET tmp.duplicate    = 1,
            your.column2     = tmp.column2
;&lt;/pre&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item></channel></rss>