<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Oratable</title>
	<atom:link href="https://www.oratable.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.oratable.com/</link>
	<description>Oracle stuff worth talking about</description>
	<lastBuildDate>Sat, 02 Jan 2021 08:09:45 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>DETERMINISTIC Functions in Oracle</title>
		<link>https://www.oratable.com/deterministic-functions-in-oracle/</link>
					<comments>https://www.oratable.com/deterministic-functions-in-oracle/#comments</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Mon, 08 Jul 2019 11:45:08 +0000</pubDate>
				<category><![CDATA[Keywords]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<guid isPermaLink="false">https://www.oratable.com/?p=1592</guid>

					<description><![CDATA[<p>Deterministic functions always produce the same output for a given input. Learn how Oracle uses the DETERMINISTIC clause for optimizing function calls.</p>
<p>The post <a href="https://www.oratable.com/deterministic-functions-in-oracle/">DETERMINISTIC Functions in Oracle</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/deterministic-functions-in-oracle/" title="Permanent link to DETERMINISTIC Functions in Oracle"><img fetchpriority="high" decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2019/07/DETERMINISTIC-functions.jpg" width="480" height="250" alt="DETERMINISTIC functions in Oracle" /></a>
</p><p>A PL/SQL function in Oracle can be tagged with a DETERMINISTIC clause, to indicate that the function will <em>always</em> produce the same output for a given input and will have no side effects.</p>
<p>A little elaboration is in order here.</p>
<h4>1. &#8230;will <em>always</em> produces the same output for a given input </h4>
<p>Let&#8217;s see this with an example: a function <a href="https://www.oratable.com/sql-to-select-rows-conditionally/">get_primary_phone</a> takes as input customer_id and returns the customer&#8217;s primary phone. Internally, the function executes SQL on a customer contact table, ranks and filters the result to get the customer&#8217;s primary phone number.</p>
<p><span id="more-1592"></span></p>
<p>If the customer&#8217;s contact information changes in the table, the value returned by get_primary_phone(customer_id) <u>will <em>not</em> produce the same output for the same input</u>. </p>
<p>This implies that get_primary_phone(customer_id) cannot be deterministic.</p>
<p>In general, any function whose output depends on the contents of variables / conditions / tables / datasets not passed as IN parameters is non-deterministic. </p>
<p>So,    <br /><a href="https://docs.oracle.com/database/121/SQLRF/functions207.htm#SQLRF06124">SYSDATE</a> is <strong>non-deterministic</strong> &#8211; the current date and time will change depending on when SYSDATE is called, but     <br /><a href="https://docs.oracle.com/database/121/SQLRF/functions096.htm#SQLRF00654">LAST_DAY</a>(date) is <strong>deterministic</strong> &#8211; for a given input date, the last date of the month will be unchanged no matter when or how many times LAST_DAY(date) is called.</p>
<p>Another eligible deterministic function: <a href="https://www.oratable.com/java-stored-procedures-in-oracle/">Java arithmetic calculation</a>.</p>
<h4>2. &#8230;will have no side effects</h4>
<p>This means that the function produces no other changes in local static variables, non-local variables, reference arguments, and performs no DML operations on other tables. </p>
<h3>What&#8217;s the use of qualifying a function as DETERMINISTIC?</h3>
<p>You may well ask. PL/SQL functions work fine without any of this DETERMINISTIC business: why, then, should we add this clause?</p>
<p>Here are a couple of reasons.</p>
<ul>
<li>
<p>The DETERMINISTIC clause is a <strong>shorthand to document a function&#8217;s special properties</strong> &#8211; that its output depends wholly on its inputs and that it produces no side effects      </p>
</li>
<li>
<p>Making a function DETERMINISTIC has a <strong>performance benefit</strong> &#8211; if the function is called multiple times within the scope of a single server call (e.g. execution of a PL/SQL block) with the same parameter values, the optimizer can choose to use the previously calculated result instead of invoking the function repeatedly. </p>
</li>
</ul>
<p>More of the performance benefit in a demo below.</p>
<h3>DETERMINISTIC functions: Syntax</h3>
<p>Just plop in the word &#8220;DETERMINISTIC&#8221; after specifying the return type of the function.</p>
<p>Example: deterministic function is_number(p_value) &#8211; this checks if a string input is a valid number or not.</p>
<pre class="brush: sql; title: ; notranslate">-- Function that tests if the input is a number
-- Returns 'Y' if number, else 'N'
create or replace function is_number 
(p_value in varchar2) 
return char 
deterministic 
is
   p_num number;
begin
   p_num := to_number(p_value);
   return 'Y';
exception
   when value_error then
      return 'N';
end;
/
</pre>
<h3>DETERMINISTIC functions: Optimization in action</h3>
<p>We&#8217;ll see how Oracle leverages a function&#8217;s deterministic property to avoid executing it multiple times for the same input.</p>
<p>For our tests, we will query a table EMPLOYEE (EMP_ID, EMP_NAME, DEPT) with 20 rows. </p>
<p>[Tests run on Oracle 11.2.0.2]</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select * from employee;

EMP_ID EMP_NAME             DEPT
------ -------------------- ----------
   100 Steven King          515
   101 Neena Kochhar        515
   102 Lex De Haan          515
   103 Alexander Hunold     590
   104 Bruce Ernst          590
   105 David Austin         A90
   106 Valli Pataballa      590
   107 Diana Lorentz        590
   108 Nancy Greenberg      515
   109 Daniel Faviet        515
   110 John Chen            515
   111 Ismael Sciarra       515
   112 Jose Manuel Urman    A1A
   113 Luis Popp            515
   114 Den Raphaely         515
   115 Alexander Khoo       515
   116 Shelli Baida         515
   117 Sigal Tobias         515
   118 Guy Himuro           515
   119 Karen Colmenares     A1A

20 rows selected.
</pre>
<p>Column DEPT in this table is a string which may or may not be a number: we will call the function is_number(dept) defined above to ascertain if DEPT is a number or not.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select emp_id
  2       , emp_name
  3       , dept
  4       , is_number(dept) is_dept_numeric
  5  from employee;

EMP_ID EMP_NAME             DEPT       IS_DEPT_NUMERIC
------ -------------------- ---------- --------------------
   100 Steven King          515        Y
   101 Neena Kochhar        515        Y
   102 Lex De Haan          515        Y
   103 Alexander Hunold     590        Y
   104 Bruce Ernst          590        Y
   105 David Austin         A90        N
   106 Valli Pataballa      590        Y
   107 Diana Lorentz        590        Y
   108 Nancy Greenberg      515        Y
   109 Daniel Faviet        515        Y
   110 John Chen            515        Y
   111 Ismael Sciarra       515        Y
   112 Jose Manuel Urman    A1A        N
   113 Luis Popp            515        Y
   114 Den Raphaely         515        Y
   115 Alexander Khoo       515        Y
   116 Shelli Baida         515        Y
   117 Sigal Tobias         515        Y
   118 Guy Himuro           515        Y
   119 Karen Colmenares     A1A        N

20 rows selected.</pre>
<p>Note that the same value of DEPT is repeated across several rows in the table: Oracle need not reevaluate the Y/N return for an input if it has done so already in the SQL for another row.</p>
<p>Without a DETERMINISTIC clause, Oracle will not be aware that it can avoid reevaluation, and proceed to execute is_number(dept) N times for N EMPLOYEE rows.</p>
<p>Let&#8217;s compare the SQL behavior before and after making the function DETERMINISTIC.</p>
<h4>Before making the function DETERMINISTIC:</h4>
<p>We will add a dbms_output.put_line to is_number(p_value), to check how many times the function gets invoked in our SQL.</p>
<p>Non-deterministic function:</p>
<pre class="brush: sql; title: ; notranslate">-- Function that tests if the input is a number
-- Returns 'Y' if number, else 'N'
create or replace function is_number 
(p_value in varchar2) 
return char 
is
   p_num number;
begin
   dbms_output.put_line('is_number ('||p_value||') called');
   p_num := to_number(p_value);
   return 'Y';
exception
   when value_error then
      return 'N';
end;
/
</pre>
<p>Compile the function:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Function that tests if the input is a number
SQL&gt; -- Returns 'Y' if number, else 'N'
SQL&gt; -- non-deterministic
SQL&gt; create or replace function is_number
  2  (p_value in varchar2)
  3  return char
  4  is
  5     p_num number;
  6  begin
  7     dbms_output.put_line('is_number ('||p_value||') called');
  8     p_num := to_number(p_value);
  9     return 'Y';
 10  exception
 11     when value_error then
 12        return 'N';
 13  end;
 14  /

Function created.
</pre>
<p>Now, run the SQL:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- SQL result when the called function
SQL&gt; -- is non-deterministic
SQL&gt; select emp_id
  2       , emp_name
  3       , dept
  4       , is_number(dept) is_dept_numeric
  5  from employee;

EMP_ID EMP_NAME             DEPT       IS_DEPT_NUMERIC
------ -------------------- ---------- --------------------
   100 Steven King          515        Y
   101 Neena Kochhar        515        Y
   102 Lex De Haan          515        Y
   103 Alexander Hunold     590        Y
   104 Bruce Ernst          590        Y
   105 David Austin         A90        N
   106 Valli Pataballa      590        Y
   107 Diana Lorentz        590        Y
   108 Nancy Greenberg      515        Y
   109 Daniel Faviet        515        Y
   110 John Chen            515        Y
   111 Ismael Sciarra       515        Y
   112 Jose Manuel Urman    A1A        N
   113 Luis Popp            515        Y
   114 Den Raphaely         515        Y
   115 Alexander Khoo       515        Y
   116 Shelli Baida         515        Y
   117 Sigal Tobias         515        Y
   118 Guy Himuro           515        Y
   119 Karen Colmenares     A1A        N

20 rows selected.

is_number (515) called
is_number (515) called
is_number (515) called
is_number (590) called
is_number (590) called
is_number (A90) called
is_number (590) called
is_number (590) called
is_number (515) called
is_number (515) called
is_number (515) called
is_number (515) called
is_number (A1A) called
is_number (515) called
is_number (515) called
is_number (515) called
is_number (515) called
is_number (515) called
is_number (515) called
is_number (A1A) called
</pre>
<p>You can see that the non-deterministic function was called once per row i.e. 20 times for 20 rows.</p>
<h4>After making the function DETERMINISTIC:</h4>
<p>We will make exactly one change to the is_number(p_value) code: add the clause DETERMINISTIC.</p>
<p>Deterministic function:</p>
<pre class="brush: sql; title: ; notranslate">-- Function that tests if the input is a number
-- Returns 'Y' if number, else 'N'
-- deterministic
create or replace function is_number 
(p_value in varchar2) 
return char 
deterministic
is
   p_num number;
begin
   dbms_output.put_line('is_number ('||p_value||') called');
   p_num := to_number(p_value);
   return 'Y';
exception
   when value_error then
      return 'N';
end;
/</pre>
<p>Compile the function:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Function that tests if the input is a number
SQL&gt; -- Returns 'Y' if number, else 'N'
SQL&gt; -- determinitic
SQL&gt; create or replace function is_number
  2  (p_value in varchar2)
  3  return char
  4  deterministic
  5  is
  6     p_num number;
  7  begin
  8     dbms_output.put_line('is_number ('||p_value||') called');
  9     p_num := to_number(p_value);
 10     return 'Y';
 11  exception
 12     when value_error then
 13        return 'N';
 14  end;
 15  /

Function created.</pre>
<p>Now, run the SQL:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- SQL result when the called function
SQL&gt; -- is deterministic
SQL&gt; select emp_id
  2       , emp_name
  3       , dept
  4       , is_number(dept) is_dept_numeric
  5  from employee;

EMP_ID EMP_NAME             DEPT       IS_DEPT_NUMERIC
------ -------------------- ---------- --------------------
   100 Steven King          515        Y
   101 Neena Kochhar        515        Y
   102 Lex De Haan          515        Y
   103 Alexander Hunold     590        Y
   104 Bruce Ernst          590        Y
   105 David Austin         A90        N
   106 Valli Pataballa      590        Y
   107 Diana Lorentz        590        Y
   108 Nancy Greenberg      515        Y
   109 Daniel Faviet        515        Y
   110 John Chen            515        Y
   111 Ismael Sciarra       515        Y
   112 Jose Manuel Urman    A1A        N
   113 Luis Popp            515        Y
   114 Den Raphaely         515        Y
   115 Alexander Khoo       515        Y
   116 Shelli Baida         515        Y
   117 Sigal Tobias         515        Y
   118 Guy Himuro           515        Y
   119 Karen Colmenares     A1A        N

20 rows selected.

is_number (515) called
is_number (515) called
is_number (590) called
is_number (A90) called
is_number (A1A) called
is_number (515) called
is_number (A1A) called</pre>
<p>Lo! The non-deterministic function was called far fewer times this time round.</p>
<p>This difference in performance with the DETERMINISTIC clause can be significant when dealing with huge volumes of data. </p>
<h3>Caution: Don&#8217;t mark non-deterministic functions as DETERMINISTIC!</h3>
<p>As the saying goes, with great power comes great responsibility. Don&#8217;t let this performance benefit tempt you into marking non-deterministic functions as deterministic. Doing so might mislead Oracle to believe that the result of the function, once calculated, is good for reuse when in fact it isn&#8217;t — and you can end up with query results that are plain inaccurate. </p>
<p>Rule of thumb: If a function uses package variables or accesses the database in any way that might affect its output, do not make it deterministic.</p>
<h3>Summary</h3>
<p>This article describes when to use the DETERMINISTIC clause with Oracle functions and what optimization you might get when marking functions as deterministic. </p>
<h3>For Further Reading</h3>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Deterministic_algorithm">Deterministic algorithm</a></li>
<li><a href="https://docs.oracle.com/database/121/LNPLS/function.htm#LNPLS01322">PL/SQL Language Reference 12c</a></li>
<li><a href="https://blogs.oracle.com/plsql-and-ebr/how-to-write-a-safe-result-cached-plsql-function">How to write a safe result-cached PL/SQL function</a></li>
<li><a href="http://stevenfeuersteinonplsql.blogspot.com/2017/05/deterministic-functions-caching-and.html">Caching and worries about consistent data</a></li>
</ul>
<p>The post <a href="https://www.oratable.com/deterministic-functions-in-oracle/">DETERMINISTIC Functions in Oracle</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/deterministic-functions-in-oracle/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Find Which Objects are INVALID in Oracle &#8211; and Why</title>
		<link>https://www.oratable.com/check-invalid-objects-in-oracle/</link>
					<comments>https://www.oratable.com/check-invalid-objects-in-oracle/#respond</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Tue, 26 Mar 2019 02:06:56 +0000</pubDate>
				<category><![CDATA[Data Dictionary]]></category>
		<category><![CDATA[DBA]]></category>
		<category><![CDATA[Exceptions]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Views]]></category>
		<guid isPermaLink="false">https://www.oratable.com/?p=1535</guid>

					<description><![CDATA[<p>Oracle objects become invalid when a dependency chain is broken. Here's how to find invalid Oracle objects and how to make them valid again.</p>
<p>The post <a href="https://www.oratable.com/check-invalid-objects-in-oracle/">Find Which Objects are INVALID in Oracle &#8211; and Why</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/check-invalid-objects-in-oracle/" title="Permanent link to Find Which Objects are INVALID in Oracle &#8211; and Why"><img decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2019/03/INVALID-objects-in-Oracle.jpg" width="480" height="250" alt="INVALID Objects in Oracle" /></a>
</p><p>It is quite normal for Oracle database objects to become INVALID, especially when a <a href="https://www.oratable.com/which-objects-refer-to-this-table/">dependency chain</a> is broken. This article takes a closer look at:</p>
<ul>
<li>typical reasons why Oracle objects become INVALID</li>
<li>how to identify INVALID objects in Oracle</li>
<li>how to convert INVALID objects to VALID</li>
</ul>
<p> <span id="more-1535"></span></p>
<h3>Why do Oracle objects become INVALID?</h3>
<p>Oracle objects often become invalid during patching or upgrades because <a href="https://www.oratable.com/which-objects-refer-to-this-table/">objects they depend on</a> get dropped or altered in some way. </p>
<p>Let&#8217;s see this in action with a table having a dependent view and procedure. </p>
<p>The table:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt;  -- Table on which other objects depend
SQL&gt;  create table temp_table
  2   ( id number);

Table created.</pre>
<p>The dependent view and procedure:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt;  -- Dependent view
SQL&gt;  create view temp_view
  2   as select * from temp_table;

View created.

SQL&gt;  -- Dependent procedure
SQL&gt;  create procedure temp_proc
  2   as
  3     l_id   number;
  4   begin
  5     select id into l_id
  6     from temp_table;
  7   end;
  8   /

Procedure created.
</pre>
<p>All the three objects &#8211; the table, the view and procedure — are valid at this time:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Check object validity
SQL&gt; select object_name
  2       , object_type
  3       , status
  4  from all_objects
  5  where object_name like 'TEMP%';

OBJECT_NAME        OBJECT_TYPE    STATUS
------------------ -------------- -------
TEMP_PROC          PROCEDURE      VALID
TEMP_TABLE         TABLE          VALID
TEMP_VIEW          VIEW           VALID
</pre>
<p>Now, we drop the table&#8230;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Drop the table; break the
SQL&gt; -- dependency chain
SQL&gt; drop table temp_table;

Table dropped.</pre>
<p>&#8230;and check the status of the view and the procedure:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Check object validity
SQL&gt; select object_name
  2       , object_type
  3       , status
  4  from all_objects
  5  where object_name like 'TEMP%';

OBJECT_NAME        OBJECT_TYPE    STATUS
------------------ -------------- -------
TEMP_PROC          PROCEDURE      INVALID
TEMP_VIEW          VIEW           INVALID</pre>
<p>Outcome: no more table — and both of the dependent objects have become invalid.</p>
<p>Another way objects become invalid is when errors occur at the time of creation or change. For example, a stored procedure gets created/replaced as valid if it compiles successfully, else it is invalid. </p>
<p>We can test this out with a procedure containing a coding error (a missing quote in dbms_output.put_line):</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Procedure with compilation error
SQL&gt; -- Will get created as INVALID; will not autocompile
SQL&gt; create or replace procedure temp_proc_with_bug
  2  as
  3  begin
  4    dbms_output.put_line('Missing closing quote!);
  5  end;
  6  /

Warning: Procedure created with compilation errors.</pre>
<p>When we check ALL_OBJECTS, we find that this procedure got created despite the error, but with status INVALID.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Check object validity
SQL&gt; select object_name
  2       , object_type
  3       , status
  4  from all_objects
  5  where object_name = 'TEMP_PROC_WITH_BUG';

OBJECT_NAME        OBJECT_TYPE    STATUS
------------------ -------------- -------
TEMP_PROC_WITH_BUG PROCEDURE      INVALID</pre>
<h3>How to identify INVALID objects in Oracle</h3>
<p>The first step is to get the list of objects and their relevant details (type, last DDL time, etc.), with a filter for status INVALID. To get this information, query [<a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/refrn/DBA_OBJECTS.html">DBA</a>/<a href="https://docs.oracle.com/database/122/REFRN/ALL_OBJECTS.htm">ALL</a>/<a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/refrn/USER_OBJECTS.html">USER</a>]_OBJECTS depending on your privileges:</p>
<blockquote>
<p>DBA_OBJECTS : All objects in the database      <br />ALL_OBJECTS : All objects accessible to the user       <br />USER_OBJECTS : All objects owned by the user</p>
</blockquote>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Check for invalid objects
SQL&gt; select object_name
  2       , object_type
  3       , status
  4  from all_objects
  5  where status = 'INVALID';

OBJECT_NAME        OBJECT_TYPE    STATUS
------------------ -------------- -------
TEST_VIEW          VIEW           INVALID
TEMP_PROC          PROCEDURE      INVALID
TEMP_PROC_WITH_BUG PROCEDURE      INVALID
</pre>
<p>The next step is to find out *why* the object is invalid. To get this information, query [<a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/refrn/DBA_ERRORS.html">DBA</a>/<a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/refrn/ALL_ERRORS.html">ALL</a>/<a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/refrn/USER_ERRORS.html">USER</a>]_ERRORS depending on your privileges:</p>
<blockquote>
<p>DBA_ERRORS : Current errors on all objects in the database      <br />ALL_ERRORS : Current errors on all objects accessible to the user       <br />USER_ERRORS : Current errors on all objects owned by the user </p>
</blockquote>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Check the error details
SQL&gt; select name
  2       , type
  3       , line
  4       , position
  5       , text
  6  from all_errors
  7  where name like 'TEMP%';

NAME                 TYPE       LINE POSITION TEXT
-------------------- --------- ----- -------- -------------------------------------------
TEMP_PROC_WITH_BUG   PROCEDURE     4       24 PLS-00103: Encountered the symbol &quot;Missing
                                              closing quote!);
                                              end;&quot; when expecting one of the following:

                                                 ( ) - + case mod new not null &lt;an identi
                                              fier&gt;
                                                 &lt;a double-quoted delimited-identifier&gt; &lt;
                                              a bind variable&gt;
                                                 table continue avg count current exists
                                              max min prior sql
                                                 stddev sum variance execute multiset the
                                               both leading
                                                 trailing forall merge year month day hou
                                              r minute second
                                                 timezone_hour timezone_minute timezone_r
                                              egion timezone_abbr
                                                 time timestamp interval date
                                                 &lt;a string literal with
 </pre>
<p>Note that not all invalid objects will have errors listed for them in *_ERRORS; some will get an entry when we make an attempt to access them. You can see this in the results of the last two SQLs: though TEMP_VIEW and TEMP_PROC are listed as invalid in ALL_OBJECTS, they are absent from ALL_ERRORS as they have not been accessed yet.</p>
<p>Let&#8217;s try to access the invalid objects and check ALL_ERRORS again.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Query the INVALID view
SQL&gt; select * from temp_view;
select * from temp_view
              *
ERROR at line 1:
ORA-04063: view &quot;HR.TEMP_VIEW&quot; has errors

SQL&gt; -- Execute the INVALID package
SQL&gt; exec temp_proc;
BEGIN temp_proc; END;

      *
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00905: object HR.TEMP_PROC is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

SQL&gt; -- Check the error details
SQL&gt; select name
  2       , type
  3       , line
  4       , position
  5       , text
  6  from all_errors
  7  where name like 'TEMP%';

NAME                 TYPE       LINE POSITION TEXT
-------------------- --------- ----- -------- -------------------------------------------
TEMP_PROC            PROCEDURE     5        3 PL/SQL: SQL Statement ignored
TEMP_PROC            PROCEDURE     6        8 PL/SQL: ORA-00942: table or view does not e
                                              xist

TEMP_PROC_WITH_BUG   PROCEDURE     4       24 PLS-00103: Encountered the symbol &quot;Missing
                                              closing quote!);
                                              end;&quot; when expecting one of the following:

                                                 ( ) - + case mod new not null &lt;an identi
                                              fier&gt;
                                                 &lt;a double-quoted delimited-identifier&gt; &lt;
                                              a bind variable&gt;
                                                 table continue avg count current exists
                                              max min prior sql
                                                 stddev sum variance execute multiset the
                                               both leading
                                                 trailing forall merge year month day hou
                                              r minute second
                                                 timezone_hour timezone_minute timezone_r
                                              egion timezone_abbr
                                                 time timestamp interval date
                                                 &lt;a string literal with

TEMP_VIEW            VIEW          0        0 ORA-00942: table or view does not exist</pre>
<p>The errors are now listed in ALL_ERRORS, since an attempt was made to query or access the invalid objects.</p>
<h3>How to convert INVALID objects to VALID</h3>
<p>Most of the time, you do not need to explicitly recompile objects. When there are no compilation failures, the object will turn from invalid to valid as soon as it is executed or accessed again.</p>
<p>To see how this works, let&#8217;s reinstate the missing object (table TEMP_TABLE) which had caused dependent objects (view TEMP_VIEW and procedure TEMP_PROC) to become invalid, and then check the status of dependent objects.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Reinstate the dropped table
SQL&gt;  create table temp_table
  2   ( id number);

Table created.

SQL&gt; -- BEFORE querying dependent view
SQL&gt; -- Check object validity
SQL&gt; select object_name
  2       , object_type
  3       , status
  4  from all_objects
  5  where object_name like 'TEMP_VIEW';

OBJECT_NAME        OBJECT_TYPE    STATUS
------------------ -------------- -------
TEMP_VIEW          VIEW           INVALID

SQL&gt; -- Query the dependent view
SQL&gt; select * from temp_view;

no rows selected

SQL&gt; -- AFTER querying dependent view
SQL&gt; -- Check object validity
SQL&gt; select object_name
  2       , object_type
  3       , status
  4  from all_objects
  5  where object_name like 'TEMP_VIEW';

OBJECT_NAME        OBJECT_TYPE    STATUS
------------------ -------------- -------
TEMP_VIEW          VIEW           VALID
</pre>
<p>&#8230;which goes to show that the invalid object fixed itself on its own.</p>
<p>If you want to compile invalid objects in advance — perhaps to distinguish between those that would auto-fix themselves and those that wouldn&#8217;t &#8211; you can follow one of the these approaches.</p>
<h4>1. ALTER&#8230;COMPILE per object</h4>
<p>Compile objects selectively, one at a time, using the syntax:</p>
<p><code>ALTER [object_type] [object_name] COMPILE; </code></p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; ALTER PROCEDURE temp_proc COMPILE;

Procedure altered.</pre>
<p>If the compilation fails, use &#8220;sho err&#8221; on SQL*Plus to display errors.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; ALTER PROCEDURE temp_proc_with_bug COMPILE;

Warning: Procedure altered with compilation errors.

SQL&gt; sho err
Errors for PROCEDURE TEMP_PROC_WITH_BUG:

LINE/COL ERROR
-------- -----------------------------------------------------------------
4/24     PLS-00103: Encountered the symbol &quot;Missing closing quote!);
         end;&quot; when expecting one of the following:
         ( ) - + case mod new not null &lt;an identifier&gt;
         &lt;a double-quoted delimited-identifier&gt; &lt;a bind variable&gt;
         table continue avg count current exists max min prior sql
         stddev sum variance execute multiset the both leading
         trailing forall merge year month day hour minute second
         timezone_hour timezone_minute timezone_region timezone_abbr
         time timestamp interval date
         &lt;a string literal with</pre>
<h4>2. DBMS_UTILITY.COMPILE_SCHEMA</h4>
<p>Use DBMS_UTILITY.COMPILE_SCHEMA to compile all object types in the specified <a href="https://www.oratable.com/oracle-user-schema-difference/">schema</a>. This takes as input a &#8220;compile_all&#8221; option: if TRUE (the default), all objects in the schema are compiled, if FALSE only invalid objects are compiled.</p>
</p>
<pre class="brush: sql; title: ; notranslate">exec dbms_utility.compile_schema(
    schema =&gt; '&lt;schema_name&gt;'
  , compile_all =&gt; FALSE -- Compile only INVALID objects
     );</pre>
<p>Let&#8217;s see this action:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; exec dbms_utility.compile_schema(schema =&gt; USER, compile_all =&gt; FALSE);

PL/SQL procedure successfully completed.

SQL&gt; -- AFTER compile_schema
SQL&gt; -- Check object validity
SQL&gt; select object_name
  2       , object_type
  3       , status
  4  from all_objects
  5  where object_name like 'TEMP%';

OBJECT_NAME        OBJECT_TYPE    STATUS
------------------ -------------- -------
TEMP_PROC          PROCEDURE      VALID
TEMP_PROC_WITH_BUG PROCEDURE      INVALID
TEMP_TABLE         TABLE          VALID
TEMP_VIEW          VIEW           VALID
</pre>
<p>The objects that remain invalid after running DBMS_UTILITY.COMPILE_SCHEMA are those that need a review and fix of errors in *_ERRORS.</p>
<h3>Summary</h3>
<p>Oracle objects typically become invalid when a dependency chain is broken, or when they have errors at the time of compilation.</p>
<p>Invalid objects can be identified by querying *_OBJECTS with a filter on status = INVALID. Error details can be queried from *_ERRORS. </p>
<p>Invalid objects get automatically recompiled when they are accessed or executed. To compile them in advance, you can use ALTER&#8230;COMPILE per object, or DBMS_UTILITY.COMPILE_SCHEMA for all objects in the schema.</p>
<h3>For Further Reading</h3>
<ul>
<li><a href="https://www.oratable.com/sqlcode-and-sqlerrm/">SQLCODE and SQLERRM</a></li>
<li><a href="https://www.oratable.com/find-your-database-name-in-oracle/">DBMS_UTILITY.GET_PARAMETER_VALUE</a> to find out your database name</li>
<li><a href="https://www.oratable.com/static-sql-vs-dynamic-sql/">DBMS_UTILITY.GET_DEPENDENCY</a> to find out object dependencies</li>
<p> <!--EndFragment--></ul>
<p>The post <a href="https://www.oratable.com/check-invalid-objects-in-oracle/">Find Which Objects are INVALID in Oracle &#8211; and Why</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/check-invalid-objects-in-oracle/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Keep the Response Body with Non-2xx Responses in UTL_HTTP</title>
		<link>https://www.oratable.com/keep-response-body-with-non-2xx-responses/</link>
					<comments>https://www.oratable.com/keep-response-body-with-non-2xx-responses/#comments</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Mon, 25 Feb 2019 09:30:25 +0000</pubDate>
				<category><![CDATA[Exceptions]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[UTL packages]]></category>
		<guid isPermaLink="false">https://www.oratable.com/?p=1524</guid>

					<description><![CDATA[<p>Web service error handling options in PL/SQL UTL_HTTP, and how to make use of SET_RESPONSE_ERROR_CHECK to keep the response body with non-2xx responses.</p>
<p>The post <a href="https://www.oratable.com/keep-response-body-with-non-2xx-responses/">How to Keep the Response Body with Non-2xx Responses in UTL_HTTP</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/keep-response-body-with-non-2xx-responses/" title="Permanent link to How to Keep the Response Body with Non-2xx Responses in UTL_HTTP"><img decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2019/02/Keep-Response-Body-non-200-HTTP-Status-in-PLSQL.jpg" width="480" height="250" alt="Keep the Response Body with Non-2xx Responses in PL/SQL" /></a>
</p><p>When <a href="https://www.oratable.com/utl_http/">UTL_HTTP web service calls</a> nicely return <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_Success">2xx status codes</a>, all is well with the world. Things get tricky when a web service call encounters an error and sends back a non-2xx response. How should the calling PL/SQL code handle this scenario? Can we read the response body from PL/SQL in case of error?</p>
<p>Here&#8217;s an overview of web service error handling options available in PL/SQL UTL_HTTP, and how to make use of SET_RESPONSE_ERROR_CHECK to keep the response body with non-200 responses.</p>
<p><span id="more-1524"></span></p>
<h3>UTL_HTTP.SET_RESPONSE_ERROR_CHECK</h3>
<p><strong>SET_RESPONSE_ERROR_CHECK</strong> is a procedure in <a href="https://www.oratable.com/plsql-package-benefits/">package</a> <a href="https://www.oratable.com/utl_http/">UTL_HTTP</a>. It determines whether or not UTL_HTTP.GET_RESPONSE will raise a PL/SQL exception when the web service returns an error status code&#160; (4xx or 5xx). </p>
<ul>
<li>
<p>If SET_RESPONSE_ERROR_CHECK is set to <strong>TRUE</strong>, UTL_HTTP.GET_RESPONSE <strong>raises a PL/SQL exception </strong>HTTP_CLIENT_ERROR (for 4xx) or HTTP_SERVER_ERROR (for 5xx).</p>
</li>
<li>
<p>If SET_RESPONSE_ERROR_CHECK is set to <strong>FALSE</strong>, UTL_HTTP.GET_RESPONSE <strong>does not raise a PL/SQL exception</strong>.</p>
</li>
</ul>
<p>By default, SET_RESPONSE_ERROR_CHECK is FALSE. With this setting, it is possible to read the HTTP response from PL/SQL even if the HTTP status code returned from the web service indicates an error (4xx or 5xx).</p>
<h3>SET_RESPONSE_ERROR_CHECK TRUE or FALSE: Which should be used?</h3>
<p>We may choose to configure SET_RESPONSE_ERROR_CHECK as TRUE or FALSE depending on factors such as:</p>
<ul>
<li>
<p><strong>Knowledge of the web service being called:</strong> What are the possible return statuses? What would be the content of the response body in case of error? </p>
<p>If a web service contains important error details in the response body, it would make sense to configure SET_RESPONSE_ERROR_CHECK as FALSE and let the PL/SQL program read and report the response.     </p>
</li>
<li>
<p><strong>Functional interpretation of return statuses: </strong>Does the web service error code truly represent an application error? &#8220;404 Not Found&#8221; may mean an invalid URL. It may also mean that a GET web service call returned no data and that&#8217;s 200&#8230;er, OK.</p>
<p>Whether we propagate web service status of HTTP_NOT_FOUND as a PL/SQL exception or not would be guided by what it represents functionally. </p>
</li>
<li>
<p><strong>The overall solution:</strong> Should the web service error be quietly handled and logged, letting the rest of the program proceed? Should the error trigger an alternate path in the code? Should it bring the entire processing to a halt? </p>
<p>How we design exception handling for web service calls with SET_RESPONSE_ERROR_CHECK would be driven by how this piece of code fits into the larger picture.</p>
</li>
</ul>
<p>The following demo shows how PL/SQL program flow changes based on the TRUE/FALSE value configured for SET_RESPONSE_ERROR_CHECK. This should help you decide the approach to follow in your solution.</p>
<h3>Demo Description: REST API GET Call via PL/SQL</h3>
<p>For the demo, we will use a simple GET call to the <a href="http://api.mathjs.org/">math.js</a> REST service. </p>
<p>HTTP GET URL <a title="http://api.mathjs.org/v4/" href="http://api.mathjs.org/v4/">http://api.mathjs.org/v4/</a>&#160; <br />Parameter &#8220;expr&#8221; = the arithmetic expression to be evaluated</p>
<p>Webservice expected responses:</p>
<p><strong></strong><strong></strong></p>
<table style="margin: 10px 0px 20px; width: 95%;" cellspacing="3" cellpadding="3" border="0">
<tbody>
<tr>
<td valign="top" width="20%"></td>
<td valign="top" width="30%"><strong>Response Status</strong></td>
<td valign="top"><strong>Response Body</strong></td>
</tr>
<tr>
<td valign="top">SUCCESS</td>
<td valign="top">200</td>
<td valign="top">Result of the expression e.g. if expr = 2+3, response is 5.</td>
</tr>
<tr>
<td valign="top">FAILURE</td>
<td valign="top">400</td>
<td valign="top">Error message</td>
</tr>
</tbody>
</table>
<p>To test the behavior with different values in SET_RESPONSE_ERROR_CHECK, we will</p>
<ol>
<li>Write two variants of a procedure <strong>call_restapi</strong> to call the REST GET request from PL/SQL: one with SET_RESPONSE_ERROR_CHECK = TRUE, another with SET_RESPONSE_ERROR_CHECK = FALSE.
<p></li>
<li><em>Test SUCCESS scenario:</em> Execute the procedure variants with a <strong>valid value of &#8220;expr&#8221;</strong> — this should show <strong>identical behavior</strong> regardless of the value of SET_RESPONSE_ERROR_CHECK.
<p></li>
<li>
<p><em>Test FAILURE scenario:</em> Execute the procedure variants with an <strong>invalid value of &#8220;expr&#8221;</strong> — this should show that:</p>
<ul>
<li>with SET_RESPONSE_ERROR_CHECK set to TRUE, the procedure raises a PL/SQL exception when return status is 400.</li>
<li>with SET_RESPONSE_ERROR_CHECK set to FALSE, the procedure completes successfully without a PL/SQL exception. The response body can be read in the procedure.</li>
</ul>
</li>
</ol>
<h3>Demo Scripts: Procedure with SET_RESPONSE_ERROR_CHECK TRUE/FALSE, Success Scenario Test, Failure Scenario Test</h3>
<h4>1. Procedure with SET_RESPONSE_ERROR_CHECK TRUE/FALSE</h4>
<p>PL/SQL procedure call_restapi uses UTL_HTTP to call the REST GET API. The code now has SET_RESPONSE_ERROR_CHECK = TRUE. We will toggle its value as TRUE/FALSE for the tests.</p>
<pre class="brush: sql; title: ; notranslate">-- REST API call demo with exception handling.
-- Evaluate a mathemathical expression via GET
-- request from PLSQL
create or replace procedure calc_restapi 
    (expression in varchar2)
is
  url           varchar2(50) := 'http://api.mathjs.org/v4/';
  request       UTL_HTTP.req;
  response      UTL_HTTP.resp;
  response_clob CLOB;
  response_buf  varchar2(32767);

begin
  -- Begin the GET request
  request := UTL_HTTP.begin_request
           (url || '?expr='|| expression
         , 'GET', 'HTTP/1.1');
   
  -- Response error check configuration
  -- TRUE =&gt; PL/SQL exception in case of response error
  -- FALSE =&gt; no PL/SQL exception
  UTL_HTTP.set_response_error_check (true);
  
  response := UTL_HTTP.get_response(request);
  
  -- Flow reaches here if a 4xx/5xx error occurs when
  -- set_response_error_check is FALSE
  if (response.status_code = UTL_HTTP.HTTP_OK) then
    dbms_output.put_line('Success: Received OK response');
  else
    dbms_output.put_line
    ('Failure: Received non-OK response: '
    ||response.status_code||' '||response.reason_phrase);
  end if;
 
  -- Create temporary LOB to hold the HTTP response
  DBMS_LOB.createtemporary(response_clob, FALSE);
  
  -- Loop to read response body chunk by chunk into CLOB
  begin
    loop
      UTL_HTTP.read_text(response
       , response_buf, 32766);
      DBMS_LOB.writeappend (response_clob
       , LENGTH(response_buf), response_buf);  
    end loop;
    
    DBMS_LOB.freetemporary(response_clob);
  exception
    when UTL_HTTP.end_of_body then
      UTL_HTTP.end_response(response);
  end;   

  dbms_output.put_line('Response : '
    ||substr(response_clob,1,200));
exception
  -- Flow reaches here if a 4xx/5xx error occurs when
  -- set_response_error_check is TRUE
  when others then
    dbms_output.put_line('Error: '
    || response.status_code ||' '
    || UTL_HTTP.GET_DETAILED_SQLCODE ||' ' 
    || substr(UTL_HTTP.GET_DETAILED_SQLERRM,1,200));
end;
/</pre>
<h4>2. SUCCESS Scenario Test Script</h4>
<p>PL/SQL script to execute call_restapi with a <strong>valid</strong> arithmetic expression as input: this should return status<strong> 200 OK</strong>. The response body should contain the result of the arithmetic expression.</p>
<p>The expected response body is the result of the expression — 8 in this case.</p>
<pre class="brush: sql; title: ; notranslate">-- Success case: status code 200 OK
-- Response body: result of expression
exec calc_restapi ('2*(7-3)');</pre>
<h4>2. FAILURE Scenario Test Script</h4>
<p>PL/SQL script to execute call_restapi with an <strong>invalid</strong> arithmetic expression as input: this should return status <strong>400 Bad Request</strong>. The response body should contain the result of the arithmetic expression.</p>
<p>The expected response body is the details of the error in the input expression.</p>
<pre class="brush: sql; title: ; notranslate">-- Failure case: 4xx error
-- Response body: error details
-- SET_RESPONSE_ERROR_CHECK = TRUE
exec calc_restapi ('a');</pre>
<p>Let&#8217;s see the demo in action.</p>
<ol>
<ul></ul>
</ol>
<h3>PL/SQL REST API GET Call with SET_RESPONSE_ERROR_CHECK&#160; = TRUE</h3>
<h4>1. Compile procedure with SET_RESPONSE_ERROR_CHECK TRUE</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- REST API call demo with exception handling.
SQL&gt; -- Evaluate a mathemathical expression via GET
SQL&gt; -- request from PLSQL
SQL&gt; -- SET_RESPONSE_ERROR_CHECK = TRUE
SQL&gt; create or replace procedure calc_restapi
  2      (expression in varchar2)
  3  is
  4    url           varchar2(50) := 'http://api.mathjs.org/v4/';
  5    request       UTL_HTTP.req;
  6    response      UTL_HTTP.resp;
  7    response_clob CLOB;
  8    response_buf  varchar2(32767);
  9
 10  begin
 11    -- Begin the GET request
 12    request := UTL_HTTP.begin_request
 13             (url || '?expr='|| expression
 14           , 'GET', 'HTTP/1.1');
 15
 16    -- Response error check configuration
 17    -- TRUE =&gt; PL/SQL exception in case of response error
 18    UTL_HTTP.set_response_error_check (true);
 19
 20    response := UTL_HTTP.get_response(request);
 21
 22    -- Flow reaches here if a 4xx/5xx error occurs when
 23    -- set_response_error_check is FALSE
 24    if (response.status_code = UTL_HTTP.HTTP_OK) then
 25      dbms_output.put_line('Success: Received OK response');
 26    else
 27      dbms_output.put_line
 28      ('Failure: Received non-OK response: '
 29      ||response.status_code||' '||response.reason_phrase);
 30    end if;
 31
 32    -- Create temporary LOB to hold the HTTP response
 33    DBMS_LOB.createtemporary(response_clob, FALSE);
 34
 35    -- Loop to read response body chunk by chunk into CLOB
 36    begin
 37      loop
 38        UTL_HTTP.read_text(response
 39         , response_buf, 32766);
 40        DBMS_LOB.writeappend (response_clob
 41         , LENGTH(response_buf), response_buf);
 42      end loop;
 43
 44      DBMS_LOB.freetemporary(response_clob);
 45    exception
 46      when UTL_HTTP.end_of_body then
 47        UTL_HTTP.end_response(response);
 48    end;
 49
 50    dbms_output.put_line('Response : '
 51      ||substr(response_clob,1,200));
 52  exception
 53    -- Flow reaches here if a 4xx/5xx error occurs when
 54    -- set_response_error_check is TRUE
 55    when others then
 56      dbms_output.put_line('Error: '
 57      || response.status_code ||' '
 58      || UTL_HTTP.GET_DETAILED_SQLCODE ||' '
 59      || substr(UTL_HTTP.GET_DETAILED_SQLERRM,1,200));
 60  end;
 61  /

Procedure created.</pre>
<h4>2. Run SUCCESS scenario test script</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Success case: status code 200 OK
SQL&gt; -- Response body: result of expression
SQL&gt; exec calc_restapi ('2*(7-3)');
Success: Received OK response
Response : 8

PL/SQL procedure successfully completed.</pre>
<h4>3. Run FAILURE scenario test script</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Failure case: 4xx error
SQL&gt; -- Response body: error details
SQL&gt; -- SET_RESPONSE_ERROR_CHECK = TRUE
SQL&gt; exec calc_restapi ('a');
Error:  -29268 ORA-29268: HTTP client error 400 - Bad Request

PL/SQL procedure successfully completed.</pre>
<p>You can see that the FAILURE test script did not execute the code after the call to UTL_HTTP.get_response, but jumped straight to the EXCEPTION section.</p>
<h3>PL/SQL REST API GET Call with SET_RESPONSE_ERROR_CHECK&#160; = FALSE</h3>
<h4>1. Compile procedure with SET_RESPONSE_ERROR_CHECK FALSE</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- REST API call demo with exception handling.
SQL&gt; -- Evaluate a mathemathical expression via GET
SQL&gt; -- request from PLSQL
SQL&gt; -- SET_RESPONSE_ERROR_CHECK = FALSE
SQL&gt; create or replace procedure calc_restapi
  2      (expression in varchar2)
  3  is
  4    url           varchar2(50) := 'http://api.mathjs.org/v4/';
  5    request       UTL_HTTP.req;
  6    response      UTL_HTTP.resp;
  7    response_clob CLOB;
  8    response_buf  varchar2(32767);
  9
 10  begin
 11    -- Begin the GET request
 12    request := UTL_HTTP.begin_request
 13             (url || '?expr='|| expression
 14           , 'GET', 'HTTP/1.1');
 15
 16    -- Response error check configuration
 17    -- FALSE =&gt; no PL/SQL exception
 18    UTL_HTTP.set_response_error_check (false);
 19
 20    response := UTL_HTTP.get_response(request);
 21
 22    -- Flow reaches here if a 4xx/5xx error occurs when
 23    -- set_response_error_check is FALSE
 24    if (response.status_code = UTL_HTTP.HTTP_OK) then
 25      dbms_output.put_line('Success: Received OK response');
 26    else
 27      dbms_output.put_line
 28      ('Failure: Received non-OK response: '
 29      ||response.status_code||' '||response.reason_phrase);
 30    end if;
 31
 32    -- Create temporary LOB to hold the HTTP response
 33    DBMS_LOB.createtemporary(response_clob, FALSE);
 34
 35    -- Loop to read response body chunk by chunk into CLOB
 36    begin
 37      loop
 38        UTL_HTTP.read_text(response
 39         , response_buf, 32766);
 40        DBMS_LOB.writeappend (response_clob
 41         , LENGTH(response_buf), response_buf);
 42      end loop;
 43
 44      DBMS_LOB.freetemporary(response_clob);
 45    exception
 46      when UTL_HTTP.end_of_body then
 47        UTL_HTTP.end_response(response);
 48    end;
 49
 50    dbms_output.put_line('Response : '
 51      ||substr(response_clob,1,200));
 52  exception
 53    -- Flow reaches here if a 4xx/5xx error occurs when
 54    -- set_response_error_check is TRUE
 55    when others then
 56      dbms_output.put_line('Error: '
 57      || response.status_code ||' '
 58      || UTL_HTTP.GET_DETAILED_SQLCODE ||' '
 59      || substr(UTL_HTTP.GET_DETAILED_SQLERRM,1,200));
 60  end;
 61  /

Procedure created.</pre>
<h4>2. Run SUCCESS scenario test script</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Success case: status code 200 OK
SQL&gt; -- Response body: result of expression
SQL&gt; exec calc_restapi ('2*(7-3)');
Success: Received OK response
Response : 8

PL/SQL procedure successfully completed.</pre>
<p>The result for a SUCCESS scenario is the same regardless of the value of SET_RESPONSE_ERROR_CHECK. </p>
<h4>3. Run FAILURE scenario test script</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Failure case: 4xx error
SQL&gt; -- Response body: error details
SQL&gt; -- SET_RESPONSE_ERROR_CHECK = FALSE
SQL&gt; exec calc_restapi ('a');
Failure: Received non-OK response: 400 Bad Request
Response : Error: Undefined symbol a</pre>
<p>You can see that the FAILURE test script executed the code after the call to UTL_HTTP.get_response, regardless of whether the web service raised an error or not. There was no PL/SQL exception &#8211; the code in the EXCEPTION block, which was executed in the previous case of SET_RESPONSE_ERROR_CHECK = TRUE, did not get executed with SET_RESPONSE_ERROR_CHECK = FALSE.</p>
<h3>Summary</h3>
<p>This article provides an overview of web service error handling options available in PL/SQL UTL_HTTP. It gives working examples of calling a REST GET API with a parameter, and shows:</p>
<ul>
<li>how to make a successful REST GET API call from PL/SQL</li>
<li>how to handle failure cases in the web service call from PL/SQL, with different settings for SET_RESPONSE_ERROR_CHECK</li>
<li>how to read the response body when the web service returns an error status (non-2xx response)</li>
</ul>
<h3>References</h3>
<ul>
<li><a href="https://docs.oracle.com/database/121/ARPLS/u_http.htm#ARPLS71102">12c Documentation</a></li>
<li><a href="https://www.oratable.com/utl_http/">UTL_HTTP for web service calls in PL/SQL</a></li>
</ul>
<p>The post <a href="https://www.oratable.com/keep-response-body-with-non-2xx-responses/">How to Keep the Response Body with Non-2xx Responses in UTL_HTTP</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/keep-response-body-with-non-2xx-responses/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Running Procedures Asynchronously with Oracle Job Scheduler</title>
		<link>https://www.oratable.com/running-procedures-asynchronously-with-oracle-job-scheduler/</link>
					<comments>https://www.oratable.com/running-procedures-asynchronously-with-oracle-job-scheduler/#comments</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Sun, 13 Jan 2019 18:20:34 +0000</pubDate>
				<category><![CDATA[Data Dictionary]]></category>
		<category><![CDATA[DBA]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<guid isPermaLink="false">https://www.oratable.com/?p=1530</guid>

					<description><![CDATA[<p>Oracle PL/SQL provides the ability to run procedures asynchronously using Oracle job scheduler DBMS_SCHEDULER. Here's how to create and run such jobs.</p>
<p>The post <a href="https://www.oratable.com/running-procedures-asynchronously-with-oracle-job-scheduler/">Running Procedures Asynchronously with Oracle Job Scheduler</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/running-procedures-asynchronously-with-oracle-job-scheduler/" title="Permanent link to Running Procedures Asynchronously with Oracle Job Scheduler"><img loading="lazy" decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2019/03/Oracle-Job-Scheduler-Async-Jobs.jpg" width="480" height="250" alt="DBMS_SCHEDULER.CREATE_JOB to Run Procedures Asynchronously" /></a>
</p><p>Consider a PL/SQL stored procedure that handles a heavy transaction. The procedure is extremely slow &#8211; when executed from a UI, the application hangs for minutes. On analysis it is found that the procedure is performing a complex series of steps, a portion of which are non-critical and need not hold up the entire transaction. In other words, it would be acceptable if:</p>
<ul>
<li>some of the steps are run asynchronously while a slimmer main transaction completes</li>
<li>failures (if any) in the asynchronous steps do not cause a failure in the main transaction</li>
</ul>
<p>Oracle PL/SQL helps us achieve these objectives with asynchronous processing using Oracle job scheduler DBMS_SCHEDULER. Here&#8217;s a demo to show you how.</p>
<p> <span id="more-1530"></span> </p>
<h3>Use Case for Demo: Movie Ticketing Application</h3>
<p>Let&#8217;s say this heavy and slow procedure is one that creates a booking in a movie ticketing application. In its current form, it does all of the following: </p>
<pre class="brush: sql; title: ; notranslate">create_booking (booking_id)
{
  allocate_seats;
  capture_customer_details;
  receive_payment;
  notify_customer;
  upsell_food;
  update_central_crm;
}
</pre>
<p><strong>The problem:</strong> The last three &#8220;non-critical&#8221; steps are slowing down the main flow. While it is desirable to have these steps work close to real-time, some lag with these steps is acceptable if it makes the main flow of create_booking faster.</p>
<pre class="brush: sql; title: ; notranslate">-- BEFORE Slow create_booking 
-- All processing synchronous
create_booking (booking_id)
{

  -- Critical parts of booking: main flow, any failure 
  -- here must fail the entire booking
  allocate_seats;
  capture_customer_details;
  receive_payment;

  -- Non-critical parts of booking: desirable to have this 
  -- as close as possible to real-time but not at the cost 
  -- of slowing down the main flow
  notify_customer;
  upsell_food;
  update_central_crm;
 }
</pre>
<p><strong>The solution:</strong> Move the last three &#8220;non-critical&#8221; steps to another procedure and invoke that procedure asynchronously. The final state should look like this algorithmically: </p>
<pre class="brush: sql; title: ; notranslate">-- AFTER Fast create_booking 
-- Non-critical processing asynchronous
create_booking (booking_id)
{

  -- Critical parts of booking: main flow, any failure 
  -- here must fail the entire booking
  allocate_seats;
  capture_customer_details;
  receive_payment;

  -- Non-critical parts of booking: wrapped in 
  -- a separate procedure called asynchronously
  &#x5B;async] post_booking_flow(booking_id);
 }

-- Async steps factored out into separate procedure
post_booking_flow (booking_id) 
{
  notify_customer;
  upsell_food;
  update_central_crm;
}
</pre>
<h3>Oracle Job Scheduler to Run Procedure Asynchronously: Summary</h3>
<p>To run a piece of code asynchronously via Oracle Job Scheduler, a summary of the steps needed: </p>
<ol>
<li>Create procedure to be run asynchronously</li>
<li>Call the procedure via <strong>DBMS_SCHEDULER.</strong><a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/arpls/DBMS_SCHEDULER.html#GUID-7E744D62-13F6-40E9-91F0-1569E6C38BBC"><strong>CREATE_JOB</strong></a> in the main flow for a single immediate run (i.e. AUTO_DROP = TRUE, the default)</li>
</ol>
<h3>Oracle Job Scheduler to Run Job Asynchronously: Stepwise Scripts</h3>
<h4>1. Create procedure to be run asynchronously</h4>
<p>This procedure is a wrapper to the portion of code in the main flow which is to be run asynchronously. </p>
<pre class="brush: sql; title: ; notranslate">-- Procedure to be run asynchronously
create or replace procedure 
post_booking_flow (booking_id in varchar2) 
as
begin
  dbms_output.put_line('START post_booking_flow');
  notify_customer;
  upsell_food;
  update_central_crm;
  dbms_output.put_line('END post_booking_flow');
end;
/</pre>
<h4>2. Call the procedure via <strong>DBMS_SCHEDULER.</strong><strong>CREATE_JOB</strong> for a single immediate run </h4>
<p>The parameter values for CREATE_JOB to support single immediate run:</p>
<ul><!--StartFragment-->   </p>
<li><em>job_name: </em>A unique name, say &#8216;post_booking_flow_job&#8217; concatenated with booking_id to prevent concurrency conflict</li>
<li><em>job_type:</em> PLSQL_BLOCK</li>
<li><em>job_action:</em> PL/SQL block invoking procedure created in step 1</li>
<li><em>enabled:</em> TRUE (default is FALSE)</li>
<li><em>auto_drop:</em> TRUE (default is TRUE)</li>
</ul>
<pre class="brush: sql; title: ; notranslate">-- Non-critical processing asynchronous
-- with DBMS_SCHEDULER.CREATE_JOB
create or replace procedure create_booking
(booking_id in varchar2)
as 
begin
  dbms_output.put_line('START create_booking');    
  -- Critical parts of booking: main flow, any failure 
  -- here must fail the entire booking
  allocate_seats;
  capture_customer_details;
  receive_payment;

  -- Non-critical parts of booking: wrapped in 
  -- a separate procedure called asynchronously
  dbms_output.put_line('Before post_booking_flow_job');
  dbms_scheduler.create_job (
  job_name   =&gt;  'post_booking_flow_job'||booking_id,
  job_type   =&gt; 'PLSQL_BLOCK',
  job_action =&gt; 
    'BEGIN 
       post_booking_flow('''||booking_id||''');
     END;',
  enabled   =&gt;  TRUE,  
  auto_drop =&gt;  TRUE, 
  comments  =&gt;  'Non-critical post-booking steps');
  
  dbms_output.put_line('After post_booking_flow_job');  
  dbms_output.put_line('END create_booking');  
end;
/
</pre>
<h3>Oracle Job Scheduler to Run Job Asynchronously: Scripts When Run</h3>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Procedure to be run asynchronously
SQL&gt; create or replace procedure
  2  post_booking_flow (booking_id in varchar2)
  3  as
  4  begin
  5    dbms_output.put_line('START post_booking_flow');
  6    notify_customer;
  7    upsell_food;
  8    update_central_crm;
  9    dbms_output.put_line('END post_booking_flow');
 10  end;
 11  /

Procedure created.
</pre>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Non-critical processing asynchronous
SQL&gt; -- with DBMS_SCHEDULER.CREATE_JOB
SQL&gt; create or replace procedure create_booking
  2  (booking_id in varchar2)
  3  as
  4  begin
  5    dbms_output.put_line('START create_booking');
  6    -- Critical parts of booking: main flow, any failure
  7    -- here must fail the entire booking
  8    allocate_seats;
  9    capture_customer_details;
 10    receive_payment;
 11
 12    -- Non-critical parts of booking: wrapped in
 13    -- a separate procedure called asynchronously
 14    dbms_output.put_line('Before post_booking_flow_job');
 15    dbms_scheduler.create_job (
 16    job_name   =&gt;  'post_booking_flow_job'||booking_id,
 17    job_type   =&gt; 'PLSQL_BLOCK',
 18    job_action =&gt;
 19      'BEGIN
 20         post_booking_flow('''||booking_id||''');
 21       END;',
 22    enabled   =&gt;  TRUE,
 23    auto_drop =&gt;  TRUE,
 24    comments  =&gt;  'Non-critical post-booking steps');
 25
 26    dbms_output.put_line('After post_booking_flow_job');
 27    dbms_output.put_line('END create_booking');
 28  end;
 29  /

Procedure created.
</pre>
<h3>Test the asynchronous PL/SQL job</h3>
<p>Pass the argument booking_id and test create_booking, now branching off into an asynchronous call to post_booking_flow_job||booking_id.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt;-- Running the procedure with async call
SQL&gt; exec create_booking('A001');
START create_booking
Before post_booking_flow_job
After post_booking_flow_job
END create_booking

PL/SQL procedure successfully completed.

SQL&gt; -- Running the procedure with async call
SQL&gt; exec create_booking('A002');
START create_booking
Before post_booking_flow_job
After post_booking_flow_job
END create_booking

PL/SQL procedure successfully completed.
</pre>
<p>Good news: the asynchronous portion of create_booking code no longer causes a lag in the booking process.</p>
<h3>Post Async Job Run: Verify Scheduler Logs</h3>
<p>ALL_SCHEDULER_JOB_LOG displays log for the Scheduler jobs accessible to the current user.</p>
<p>ALL_SCHEDULER_JOB_RUN_DETAILS displays run details for the Scheduler jobs accessible to the current user.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Job logs generated in the last hour
SQL&gt; select job_name, status
  2  from all_scheduler_job_log
  3  where job_name like 'POST_BOOKING_FLOW_JOB%'
  4  and log_date &gt; sysdate - 1/24
  5  order by log_date desc;

JOB_NAME                  STATUS
------------------------- ----------
POST_BOOKING_FLOW_JOBA002 SUCCEEDED
POST_BOOKING_FLOW_JOBA001 SUCCEEDED

SQL&gt; -- Job run details generated in the last hour
SQL&gt; select job_name, status
  2  from all_scheduler_job_run_details
  3  where job_name like 'POST_BOOKING_FLOW_JOB%'
  4  and log_date &gt; sysdate - 1/24
  5  order by log_date desc;

JOB_NAME                  STATUS
------------------------- ----------
POST_BOOKING_FLOW_JOBA002 SUCCEEDED
POST_BOOKING_FLOW_JOBA001 SUCCEEDED
</pre>
<h3>Scheduler Privileges to Create Job Asynchronously</h3>
<p>In addition to <strong>EXECUTE</strong> privilege on the package <strong>DBMS_SCHEDULER</strong>, one must have:</p>
<ul>
<li><strong>CREATE JOB</strong> privilege to create a job in one&#8217;s own <a href="https://www.oratable.com/oracle-user-schema-difference/">schema</a>, and </li>
<li><strong>CREATE ANY JOB</strong> privilege to create a job in any schema except SYS</li>
</ul>
<p>To invoke a PL/SQL program when creating a job, the owner of the job must be the owner of the program or have EXECUTE privilege on the program. </p>
<h3>For Further Reading</h3>
<p>Running procedures asynchronously is just one use case for DBMS_SCHEDULER &#8211; look up <a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/admin/scheduling-jobs-with-oracle-scheduler.html">Oracle 12c documentation</a> for the range of its features and utilities. </p>
<ul></ul>
<p>Also see: <a href="http://stevenfeuersteinonplsql.blogspot.com/2014/08/plsql-challenge-community-to-rescue-get.html">Get job name inside DBMS_SCHEDULER executed procedure</a></p>
<ul></ul>
<p>The post <a href="https://www.oratable.com/running-procedures-asynchronously-with-oracle-job-scheduler/">Running Procedures Asynchronously with Oracle Job Scheduler</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/running-procedures-asynchronously-with-oracle-job-scheduler/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Pagination Queries in Oracle: Analytics, Row Limiting Clause</title>
		<link>https://www.oratable.com/pagination-queries-analytics-row-limiting/</link>
					<comments>https://www.oratable.com/pagination-queries-analytics-row-limiting/#comments</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Tue, 20 Nov 2018 10:30:07 +0000</pubDate>
				<category><![CDATA[Analytics]]></category>
		<category><![CDATA[Keywords]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Subqueries]]></category>
		<guid isPermaLink="false">http://www.oratable.com/?p=1502</guid>

					<description><![CDATA[<p>How to implement pagination queries in Oracle, using (1) analytic functions (2) Oracle's row limiting clause to fetch N rows from a given offset.</p>
<p>The post <a href="https://www.oratable.com/pagination-queries-analytics-row-limiting/">Pagination Queries in Oracle: Analytics, Row Limiting Clause</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/pagination-queries-analytics-row-limiting/" title="Permanent link to Pagination Queries in Oracle: Analytics, Row Limiting Clause"><img loading="lazy" decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2018/11/Pagination-Queries-in-Oracle-OFFSET-FETCH.jpg" width="480" height="250" alt="Pagination Queries in Oracle: Analytics, Row Limiting Clause" /></a>
</p><p>Pagination is the process of dividing query results (typically on a user interface) into discrete pages, where each page contains a smaller/more manageable number of rows. In classic offset pagination, each page shows a fixed count of rows (say N): if N is 20, then the first page shows 1-20 rows, a &#8220;Next&#8221; navigation moves control to the second page with 21-40 rows, and so on.</p>
<p>This article demonstrates how to write queries to achieve this style of pagination in Oracle. </p>
<p><span id="more-1502"></span></p>
<p>Two solutions are presented below with examples. The first uses <a href="https://www.oratable.com/category/analytics/">analytic functions</a> and is compatible with pre-12c versions of Oracle. The second, more compact solution uses Oracle&#8217;s row limiting clause.</p>
<h3>Pagination query — setting the context</h3>
<p>Pagination queries need some way to identify the start and end markers for rows to be returned for a page. Let&#8217;s say this is done using these variables:</p>
<ul>
<li><strong>PAGE_SIZE: </strong>Count of rows to be returned by the query per fetch / number of rows to be displayed on a single page.
</li>
<li><strong>PAGE_NUM: </strong>Page number for which the result set is required. This determines the starting position from which the ordered rows are to be returned.
<p>Starting position is calculated as:       <br />((PAGE_NUM — 1) * PAGE_SIZE) + 1       </p>
<p>e.g. if PAGE_SIZE = 20:       <br />For PAGE_NUM = 1, starting position = 1.       <br />For PAGE_NUM = 2, starting position = 21.       <br />For PAGE_NUM = 3, starting position = 41.</li>
</ul>
<p>For the demo, we will define PAGE_SIZE and PAGE_NUM as bind variables&#8230;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Count of rows to be returned by the SQL per fetch
SQL&gt; var PAGE_SIZE number

SQL&gt; -- Page number for which the result set is required
SQL&gt; var PAGE_NUM number
</pre>
<p>&#8230;and assign them values:</p>
<pre class="brush: sql; title: ; notranslate">
SQL&gt; exec :PAGE_SIZE := 5

PL/SQL procedure successfully completed.

SQL&gt; exec :PAGE_NUM  := 2

PL/SQL procedure successfully completed.
</pre>
<p>With PAGE_SIZE = 5 and PAGE_NUM = 2, the query should fetch row numbers 6-10 from the result set.</p>
<p>Let&#8217;s see the pagination queries in action.</p>
<h3>Pre-12c: Pagination with Analytic Function</h3>
<p>The solution performs the following steps to get a specific subset of rows from the query results.</p>
<ol>
<li><a href="https://www.oratable.com/oracle-order-by-clause/">Order</a> the result set and apply <a href="https://www.oratable.com/category/analytics/">analytic function</a> ROW_NUMBER to assign a running counter 1..N (say &#8220;row_index&#8221;) to each row. </li>
<li>Use the above result set as an <a href="https://www.oratable.com/inline-views/">inline view</a>, and pick from it only those rows where row_index lies in the window defined by   <br /><font face="Courier New">[((PAGE_NUM — 1) * PAGE_SIZE) + 1]</font> and<br /><font face="Courier New">[PAGE_NUM * PAGE_SIZE]</font>. </li>
</ol>
<p>The SQL:</p>
<pre class="brush: sql; title: ; notranslate">-- Pre-12c Pagination SQL with analytic function
-- Bind variables:
-- PAGE_SIZE: Count of rows to be returned = 5
-- PAGE_NUM: Page number = 2
select  object_id, object_name  
from (select  a.object_id, a.object_name,
      row_number() over (order by a.object_id) row_index
      from dba_objects a
      )
where row_index between 
              ((:PAGE_NUM — 1) * :PAGE_SIZE) + 1 
          and (:PAGE_NUM * :PAGE_SIZE);
</pre>
<p>When run:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Pre-12c Pagination SQL with analytic function
SQL&gt; -- Bind variables:
SQL&gt; -- PAGE_SIZE: Count of rows to be returned = 5
SQL&gt; -- PAGE_NUM: Page number = 2
SQL&gt; select  object_id, object_name
  2  from (select  a.object_id, a.object_name,
  3        row_number() over (order by a.object_id) row_index
  4        from dba_objects a
  5        )
  6  where row_index between
  7                ((:PAGE_NUM - 1) * :PAGE_SIZE) + 1
  8            and (:PAGE_NUM * :PAGE_SIZE);

 OBJECT_ID OBJECT_NAME
---------- ---------------------
        13 UET$
        14 SEG$
        15 UNDO$
        16 TS$
        17 FILE$
</pre>
<p>Oracle 12c+ provides a more compact way of implementing the same functionality, using what&#8217;s called the row limiting clause.</p>
</ol>
<h3>12c: Row Limiting Clause with OFFSET/FETCH</h3>
<p>The row limiting clause, with its support for ANSI-standard FETCH FIRST/NEXT and OFFSET clauses, eliminates the need for an inline view.</p>
<p>Syntax:</p>
<p style="line-height: 1.2em;"><font face="Courier New"><strong>select</strong> [columns]       <br /><strong>from</strong> [table]       <br /><strong>order by</strong> [key]       <br /><strong>offset</strong> [starting point for the FETCH — 1]       <br /><strong>fetch next</strong> [count of rows to be returned i.e. PAGE_SIZE] <strong>rows only</strong>;</font>&#160;&#160;&#160;&#160;&#160;&#160;&#160; </p>
<p>Fetch can be specified in terms of percent as well, with <font face="Courier New">percent only</font> in place of <font face="Courier New">rows only. </font></p>
<p>The SQL:</p>
<pre class="brush: sql; title: ; notranslate">-- 12c Pagination SQL with OFFSET/FETCH
-- Bind variables:
-- PAGE_SIZE: Count of rows to be returned = 5
-- PAGE_NUM: Page number = 2
select object_id, object_name
from dba_objects 
order by object_id
offset (:PAGE_NUM - 1) * :PAGE_SIZE rows 
fetch next :PAGE_SIZE rows only;	</pre>
<p>When run:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- 12c Pagination SQL with OFFSET/FETCH
SQL&gt; -- Bind variables:
SQL&gt; -- PAGE_SIZE: Count of rows to be returned = 5
SQL&gt; -- PAGE_NUM: Page number = 2
SQL&gt; select object_id, object_name
  2  from dba_objects
  3  order by object_id
  4  offset (:PAGE_NUM - 1) * :PAGE_SIZE rows
  5  fetch next :PAGE_SIZE rows only;

 OBJECT_ID OBJECT_NAME
---------- ---------------------
        13 UET$
        14 SEG$
        15 UNDO$
        16 TS$
        17 FILE$
</pre>
<p>Note: With row limiting clause, 12c+ essentially provides a simplified way to represent the same query as you might use in a pre-12c database. Under the surface, Oracle applies query transformation to rewrite the row limiting clause to use analytics. </p>
<h3>Summary</h3>
<p>This article illustrates two ways of implementing pagination (i.e. rows N through M of a result set) in Oracle:</p>
<ol>
<li>SQL query with filter on analytic function row_number()</li>
<li>SQL query with row limiting clause [available Oracle 12c onwards]</li>
</ol>
<h3>For Further Reading</h3>
<ul>
<li><a href="https://oracle-base.com/articles/12c/row-limiting-clause-for-top-n-queries-12cr1">ORACLE-BASE: Paging Through Data</a></li>
<li><a href="https://jonathanlewis.wordpress.com/2013/07/10/12c-first-n/">12c First N</a></li>
<li><a href="https://www.oratable.com/nth-highest-salary-in-oracle/">Pre-12c Nth Highest Salary</a></li>
<li><a href="https://blogs.oracle.com/oraclemagazine/on-oracle-database-12c,-part-1">Oracle Magazine (Oct 2013): Oracle Database 12c Part 1 Features</a></li>
</ul>
<p>The post <a href="https://www.oratable.com/pagination-queries-analytics-row-limiting/">Pagination Queries in Oracle: Analytics, Row Limiting Clause</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/pagination-queries-analytics-row-limiting/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Conditional Unique Index on Multiple Columns</title>
		<link>https://www.oratable.com/conditional-unique-index-on-multiple-columns/</link>
					<comments>https://www.oratable.com/conditional-unique-index-on-multiple-columns/#comments</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Tue, 09 Oct 2018 10:00:07 +0000</pubDate>
				<category><![CDATA[Database Design]]></category>
		<category><![CDATA[DBA]]></category>
		<category><![CDATA[DDL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[case expression]]></category>
		<category><![CDATA[constraints]]></category>
		<guid isPermaLink="false">http://www.oratable.com/?p=1495</guid>

					<description><![CDATA[<p>Oracle's function-based indexes can be used to build conditional uniqueness on table columns. Here's a working example of a conditional unique index on multiple columns.</p>
<p>The post <a href="https://www.oratable.com/conditional-unique-index-on-multiple-columns/">Conditional Unique Index on Multiple Columns</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/conditional-unique-index-on-multiple-columns/" title="Permanent link to Conditional Unique Index on Multiple Columns"><img loading="lazy" decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2018/10/Conditional-Unique-Index-on-Multiple-Columns.jpg" width="480" height="250" alt="Conditional unique index on multiple columns" /></a>
</p><p>When we need to enforce uniqueness on a combination of table columns *only if* a certain condition is true, Oracle helps us implement this using a function-based index.</p>
<p><span id="more-1495"></span></p>
<h3>What is a Function-Based Index?</h3>
<p>A <strong>function-based index</strong> is created on the value of a function or expression. The function used for building the index can be an arithmetic expression or an expression that contains a SQL function, user-defined PL/SQL function, package function, or C callout.</p>
<p>A typical use case for implementing a function-based index is case-insensitive search. Let&#8217;s say the column <code>first_name</code> in table <code>employees</code> contains mixed-case characters. If we have a standard index on <code>first_name</code>, and we use UPPER(<code>first_name)</code> in the WHERE clauses, the standard index will get ignored. We resolve this problem with this function-based index on the <code>employees</code> table:</p>
<p><font face="Courier New">CREATE INDEX emp_idx_f1      <br /> ON employees(UPPER(first_name));</font> </p>
<p>Let&#8217;s see next how a function-based index can help with building conditional uniqueness on multiple columns in a table.</p>
<h3>Function-based Index for Conditional Uniqueness</h3>
<p>Take the case of a table that stores the <a href="https://www.oratable.com/data-modeling/">audit trail of changes</a> in additional to the latest active record:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Table that stores audit trail of
SQL&gt; -- changes in addition to the active row
SQL&gt; create table demo_fbi (
  2     col1 number
  3   , col2 number
  4   , col3 varchar2(30)
  5   , created_date date
  6   , active_flag char(1) default 'Y'
  7       check (active_flag in ('Y','N')));

Table created.
</pre>
<p>In the table above, we want (col1, col2) to work as a composite unique key conditionally i.e. only if active_flag = &#8216;Y&#8217;. There can be multiple occurrences of the same (col1, col2) combination for rows with active_flag = &#8216;N&#8217;.</p>
<p>We implement this using <a href="https://www.oratable.com/simple-case-searched-case/">CASE WHEN</a> ensconced in the <a href="https://www.oratable.com/unique-constraint-vs-unique-index/">CREATE UNIQUE INDEX</a> statement:</p>
<pre class="brush: sql; title: ; notranslate">
SQL&gt; -- Conditional unique index on multiple columns
SQL&gt; create unique index demo_fbi_idx
  2  on demo_fbi
  3    (case when active_flag = 'Y' then
  4              col1 else null end,
  5     case when active_flag = 'Y' then
  6              col2 else null end);

Index created.
</pre>
<p>Here the <a href="https://www.oratable.com/simple-case-searched-case/">searched CASE</a> construct builds the index on (col1,col2) only if active_flag = &#8216;Y&#8217;, else it sets the value to (null,null) &#8211; note that in Oracle, <a href="https://docs.oracle.com/cd/E11882_01/server.112/e40540/indexiot.htm">all-NULL index entries are not recorded</a>. This meets the desired result of having an index on (col1,col2) only if active_flag = &#8216;Y&#8217;.</p>
<h4>Let&#8217;s test this!</h4>
<p>We will test the behavior of the conditional unique index on multiple columns, with an insert statement script:</p>
<pre class="brush: sql; title: ; notranslate">-- 1. Insert valid row
-- (1,1) for inactive row
insert into demo_fbi values
(1, 1, 'TEST1', sysdate,'N');

-- 2. Insert valid rows
-- (1,1) again for inactive row
insert into demo_fbi values
(1, 1, 'TEST12', sysdate,'N');

-- 3. Insert valid row
-- (1,1) for active row
insert into demo_fbi values
(1, 1, 'TEST3', sysdate,'Y');

-- 4. Insert invalid row
-- (1,1) again for active row
insert into demo_fbi values
(1, 1, 'TEST4', sysdate,'Y');

-- 5. Insert valid row
-- (1,2) for active row
insert into demo_fbi values
(1, 2, 'TEST5', sysdate,'Y');

-- 6. Insert invalid row
-- (1,2) again for active row
insert into demo_fbi values
(1, 2, 'TEST6', sysdate,'Y');
</pre>
<p>The script above attempts to insert 6 rows, of which 2 are expected to fail due to conditional unique index violation on (col1, col2). The details of rows expected to fail are given as comments in the script.</p>
<p>When run:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- 1. Insert valid row
SQL&gt; -- (1,1) for inactive row
SQL&gt; insert into demo_fbi values
  2  (1, 1, 'TEST1', sysdate,'N');

1 row created.

SQL&gt;
SQL&gt; -- 2. Insert valid rows
SQL&gt; -- (1,1) again for inactive row
SQL&gt; insert into demo_fbi values
  2  (1, 1, 'TEST2', sysdate,'N');

1 row created.

SQL&gt;
SQL&gt; -- 3. Insert valid row
SQL&gt; -- (1,1) for active row
SQL&gt; insert into demo_fbi values
  2  (1, 1, 'TEST3', sysdate,'Y');

1 row created.

SQL&gt;
SQL&gt; -- 4. Insert invalid row
SQL&gt; -- (1,1) again for active row
SQL&gt; insert into demo_fbi values
  2  (1, 1, 'TEST4', sysdate,'Y');
insert into demo_fbi values
*
ERROR at line 1:
ORA-00001: unique constraint (HR.DEMO_FBI_IDX)
violated


SQL&gt;
SQL&gt; -- 5. Insert valid row
SQL&gt; -- (1,2) for active row
SQL&gt; insert into demo_fbi values
  2  (1, 2, 'TEST5', sysdate,'Y');

1 row created.

SQL&gt;
SQL&gt; -- 6. Insert invalid row
SQL&gt; -- (1,2) again for active row
SQL&gt; insert into demo_fbi values
  2  (1, 2, 'TEST6', sysdate,'Y');
insert into demo_fbi values
*
ERROR at line 1:
ORA-00001: unique constraint (HR.DEMO_FBI_IDX)
violated
</pre>
<p>The result is as expected — 4 rows were inserted, and two did not pass the conditional unique index validation.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select * from demo_fbi;

      COL1       COL2 COL3   CREATED_D ACTIVE_FLAG
---------- ---------- ------ --------- -----------
         1          1 TEST1  02-OCT-18 N
         1          1 TEST2  02-OCT-18 N
         1          1 TEST3  02-OCT-18 Y
         1          2 TEST5  02-OCT-18 Y
</pre>
<h3>Summary</h3>
<p>Oracle&#8217;s function-based indexes can be used to build conditional uniqueness on table columns. This article shows how a conditional unique index works on a table that stores, along with its active records, the audit trail of changes to its records.</p>
<p>The post <a href="https://www.oratable.com/conditional-unique-index-on-multiple-columns/">Conditional Unique Index on Multiple Columns</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/conditional-unique-index-on-multiple-columns/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Java Stored Procedures in Oracle</title>
		<link>https://www.oratable.com/java-stored-procedures-in-oracle/</link>
					<comments>https://www.oratable.com/java-stored-procedures-in-oracle/#respond</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Wed, 03 Oct 2018 02:00:41 +0000</pubDate>
				<category><![CDATA[Datatypes]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[java]]></category>
		<guid isPermaLink="false">http://www.oratable.com/?p=1491</guid>

					<description><![CDATA[<p>On why you would want Java stored procedures in Oracle, and how to store and invoke these stored procedures via call specifications.</p>
<p>The post <a href="https://www.oratable.com/java-stored-procedures-in-oracle/">Java Stored Procedures in Oracle</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/java-stored-procedures-in-oracle/" title="Permanent link to Java Stored Procedures in Oracle"><img loading="lazy" decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2018/09/Java-Stored-Procedures-in-Oracle.jpg" width="480" height="250" alt="Java Stored Procedures in Oracle" /></a>
</p><p>Some solution requirements are best addressed with Java code in the Oracle database. This article shows you use cases where Java methods in Oracle are useful, how Oracle lets you store Java methods and how they can be called from SQL or PL/SQL.</p>
<p><span id="more-1491"></span></p>
<h3>Java stored procedures in Oracle: Why do you need them?</h3>
<p>A common reason why you would want Java stored procedures in Oracle is that the equivalent PL/SQL is not feasible, performant or economical to build. A few use cases: </p>
<ul>
<li>Application-specific code that&#8217;s already in use in Java and needs to be migrated to the database: it might make more sense to make use of the working Java code than to recreate it from scratch in PLSQL.</li>
<li>Out-of-the-box Java functions: Java packages might have pre-built functions for the problem at hand, and the cost of building their PL/SQL equivalent is prohibitively high. This could range from simple stuff such as primality testing with <a href="https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html">BigInteger</a> or advanced cryptographic operations with <a href="https://docs.oracle.com/javase/8/docs/api/javax/crypto/package-use.html">javax.crypto</a>.</li>
</ul>
<h3>How to store and invoke Java methods in Oracle</h3>
<p>The steps are outlined below. A working example follows in the next section.</p>
<h4>1. Create the Java source</h4>
<p>Write the code (<em>java_source_code</em>) as you normally would in Java. Give the Java source code a name (<em>java_source_name</em>) and create it in the database, with the syntax:</p>
<p><font face="Courier New">create or replace and resolve java source named<br /> &quot;<strong>&lt;java_source_name&gt;</strong>&quot; as&#160; <br /><font face="Courier New"><strong>&lt;java_source_code&gt;</strong>;</font></font> </p>
<h4>2. Publish the Java method through a call specification</h4>
<p>To publish the Java method, create a &#8220;call specification&#8221; that maps the Java method name, parameter types and return types to their SQL counterparts. The syntax:</p>
<p><font face="Courier New">create or replace function <strong>&lt;plsql_function_name&gt;</strong>       <br />&#160; (<font style="background-color: rgb(204, 204, 204);">&lt;plsql_parameters&gt;</font>)       <br />&#160; return <font style="background-color: rgb(204, 204, 204);">&lt;plsql_return_type&gt;</font>       <br /> as language java       <br /> name &lsquo;<strong>&lt;java_source_name&gt;.&lt;method&gt;</strong>       <br /><font face="Courier New">&#160; (<font style="background-color: rgb(204, 204, 204);">&lt;java_parameters&gt;</font>)         <br />&#160; return <font style="background-color: rgb(204, 204, 204);">&lt;java_return_type&gt;</font>&rsquo;;</font></font></p>
<p>There should be a 1:1 correspondence between the java parameters/return types and the SQL counterparts. For more on mappings and data conversion considerations, see the Oracle documentation for <a href="https://docs.oracle.com/database/121/JJDBC/datacc.htm#JJDBC28365">mappings between SQL Types and Java Types</a> (12c).</p>
<p><em>A call specification is not a wrapper.</em> A wrapper adds another layer of execution, whereas a call specification only publishes the existence of a Java method. So when you call the Java method via its call specification, this happens at runtime with minimal overhead.</p>
<h4>3. Invoke the Java method via the call specification</h4>
<p>The stored Java method can now be called using <font face="Courier New">&lt;plsql_function_name&gt;</font> like any regular PL/SQL function. The Java stored procedure can accept arguments, reference Java classes, and return Java result values. </p>
<h3>Java in Oracle: Demo</h3>
<p>The method used for this demo is <code>BigInteger</code>&#8216;s <a href="https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html#nextProbablePrime()">nextProbablePrime</a>, a pre-built solution that returns the first probable prime integer greater than the current value.</p>
<p>Before moving on to the demo, a little introduction to <code>BigInteger</code>: this is a custom Java class for handling very large numbers, with pre-built methods for mathematical operations. Read more about it in the <a href="https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html">Java 8 documentation</a>. Note that the class <code>BigInteger </code>does not have a direct datatype mapping in PL/SQL, and so we need to convert the method&#8217;s input/output to a Java <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">primitive type</a>, which can be then mapped to the PL/SQL datatype NUMBER. </p>
<p>Let&#8217;s see in action, the steps to store and invoke a Java method in Oracle.</p>
<h4>1. Create the Java source</h4>
<p>Java source <code>MathFunction</code> with method <code>nextProbablePrime</code> — this is a wrapper for <code>BigInteger.nextProbablePrime</code>, with a switch of datatype: <code>int </code>from/to <code>BigInteger</code>.</p>
<pre class="brush: sql; title: ; notranslate">-- Java source with method which returns the first 
-- integer greater than the input that's probably prime. 
create or replace and resolve java source named 
&quot;MathFunction&quot; as
import java.math.*;

/**
 *  Java class to calculate math functions
 */
public class MathFunction {

      /**
       * Wrapper for BigInteger's nextProbablePrime
       * function. Input as well as return type are
       * are mapped from int to/from BigInteger.
       * 
       * @param int n
       * @return int next probable prime &gt; n
       */
       public static int nextProbablePrime(int n) {

            return BigInteger.valueOf(n)
                    .nextProbablePrime().intValue();
       }
    };
/
</pre>
<p>When run:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Java source with method which returns the first
SQL&gt; -- integer greater than the input that's probably prime.
SQL&gt; create or replace and resolve java source named
  2  &quot;MathFunction&quot; as
  3  import java.math.*;
  4
  5  /**
  6   *  Java class to calculate math functions
  7   */
  8  public class MathFunction {
  9
 10        /**
 11         * Wrapper for BigInteger's nextProbablePrime
 12         * function. Input as well as return type are
 13     * are mapped from int to/from BigInteger.
 14         *
 15         * @param int n
 16         * @return next probable prime &gt; n
 17         */
 18         public static int nextProbablePrime(int n) {
 19
 20              return BigInteger.valueOf(n)
 21                      .nextProbablePrime().intValue();
 22         }
 23      };
 24  /

Java created.

SQL&gt;
</pre>
<h4>2. Publish the Java method through a call specification</h4>
<p>The call specification for the source created in step 1 -&#160; <code>MathFunction.nextProbablePrime(int)</code> with return type <code>int</code>.</p>
<pre class="brush: sql; title: ; notranslate">-- Call specification for the Java source 
-- MathFunction.nextProbablePrime
create or replace function nextProbablePrime 
  (n number)
  return number
as language java
name 'MathFunction.nextProbablePrime(int) 
  return int';
</pre>
<p>When run:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Call specification for the Java source
SQL&gt; -- MathFunction.nextProbablePrime
SQL&gt; create or replace function nextProbablePrime
  2    (n number)
  3    return number
  4  as language java
  5  name 'MathFunction.nextProbablePrime(int)
  6    return int';
  7  /

Function created.

SQL&gt;</pre>
<h4>3. Invoke the Java method via the call specification</h4>
<p>The Java stored method is now ready for use via its call specification.</p>
<p>Examples:</p>
<pre class="brush: sql; title: ; notranslate">
SQL&gt; select nextProbablePrime(36) from dual;

NEXTPROBABLEPRIME(36)
---------------------
                   37

SQL&gt; select nextProbablePrime(2147) from dual;

NEXTPROBABLEPRIME(2147)
-----------------------
                   2153
</pre>
<h3>Summary</h3>
<p>Java methods can be stored in Oracle and invoked via a call specification. This article shows you how this can be done, with an example using the Java method BigInteger.nextProbablePrime&#160; in the Oracle database.</p>
<p>The post <a href="https://www.oratable.com/java-stored-procedures-in-oracle/">Java Stored Procedures in Oracle</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/java-stored-procedures-in-oracle/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>REGEX to Split a Comma-Separated String into Rows</title>
		<link>https://www.oratable.com/regex-split-comma-separated-string/</link>
					<comments>https://www.oratable.com/regex-split-comma-separated-string/#comments</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Mon, 03 Sep 2018 10:15:47 +0000</pubDate>
				<category><![CDATA[SQL]]></category>
		<category><![CDATA[hierarchical queries]]></category>
		<category><![CDATA[regex]]></category>
		<guid isPermaLink="false">http://www.oratable.com/?p=1488</guid>

					<description><![CDATA[<p>How to split a comma-separated string into rows in Oracle SQL, using regex function REGEX_SUBSTR and recursion via CONNECT BY.</p>
<p>The post <a href="https://www.oratable.com/regex-split-comma-separated-string/">REGEX to Split a Comma-Separated String into Rows</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/regex-split-comma-separated-string/" title="Permanent link to REGEX to Split a Comma-Separated String into Rows"><img loading="lazy" decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2018/08/REGEX-to-Split-String-to-Rows.jpg" width="480" height="250" alt="REGEX to Split String into Rows" /></a>
</p><p>A typical scenario that involves splitting a comma-separated string into rows: </p>
<ul>
<li>in a UI-driven query, the user would enter a list of IDs into an input box, and</li>
<li>the application should retrieve details for the input list of IDs</li>
</ul>
<p><img loading="lazy" decoding="async" title="Search Input box" style="border: 0px currentcolor; border-image: none; float: left; display: inline; background-image: none;" border="0" alt="Search Input box" src="https://www.oratable.com/wp-content/uploads/2018/08/Search-Input-box.png" width="273" align="left" height="103" />The count of IDs in the list can vary — the user may enter a single value or multiple, and the underlying SQL should fetch the details for all of them.</p>
<p>This sounds simple enough on the face of it: bind a parameter to a comma-separated list of values such as &#8216;A1,A2,A4&#8217; and then look for corresponding rows using SQL for those IDs.</p>
<p>There&#8217;s a catch though.</p>
<p><span id="more-1488"></span></p>
<h3>Value in String != Value in List</h3>
<p>Let&#8217;s say this is the table data:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; select * from customer;

ID NAME
-- --------------------
A1 Jim
A2 Michael
A3 Pam
A4 Kevin
A5 Angela</pre>
<p>&#8230;and we need to get the data for ids A1,A2 and A4 from the table. </p>
<p>The user would enter A1,A2,A4 into the search screen and the application would apply it in SQL to match the data in the table.</p>
<p>Directly plugging the values from the search screen&#8217;s input box into the query does not work:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Customers with key filter
SQL&gt; -- Wrong SQL!
SQL&gt; select *
  2  from customer
  3  where id in ('A1,A2,A4');

no rows selected</pre>
<p>The SQL should instead be looking into a set of discrete values via the IN clause, as in&#8230;</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Customers with key filter
SQL&gt; -- That's the SQL we want!
SQL&gt; select *
  2  from customer
  3  where id in ('A1','A2','A4');

ID NAME
-- --------------------
A1 Jim
A2 Michael
A4 Kevin</pre>
<h3>The Classic String to Rows Conundrum</h3>
<p>What was entered into the input box was a string: &#8216;A1, A2, A4&#8217;.</p>
<p>&#8216;A1, A2, A4&#8217; as-is in SQL IN will be interpreted as one unit of text — not as a list of discrete values &#8216;A1&#8217;, &#8216;A2&#8217;, and &#8216;A4&#8217;.</p>
<p><em>We need a way to convert the comma-separated text to rows.</em><br />How do we do it? <br />There are several ways. This article shows you a simple one, using a combination of <a href="https://www.oratable.com/regular-expressions-in-oracle/">REGEX_SUBSTR</a> and <a href="https://www.oratable.com/connect-by-hierarchical-query/">CONNECT BY</a>.</p>
<h3>SQL: String to Rows Solution</h3>
<p>The SQL that does get the result we seek:</p>
<pre class="brush: sql; title: ; notranslate">-- Customers with key filter
-- REGEXP_SUBSTR and CONNECT BY to convert string to rows
select  *
from customer 
where id in
  (select regexp_substr('A1,A2,A4','&#x5B;^,]+', 1, level) 
   from dual 
   connect BY regexp_substr('A1,A2,A4', '&#x5B;^,]+', 1, level) 
   is not null);</pre>
<p>When run:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Customers with key filter
SQL&gt; -- REGEXP_SUBSTR and CONNECT BY to convert string to rows
SQL&gt; select  *
  2  from customer
  3  where id in
  4    (select regexp_substr('A1,A2,A4','&#x5B;^,]+', 1, level)
  5     from dual
  6     connect BY regexp_substr('A1,A2,A4', '&#x5B;^,]+', 1, level)
  7     is not null);

ID NAME
-- --------------------
A1 Jim
A2 Michael
A4 Kevin
</pre>
<p>The trick is in the <a href="https://www.oratable.com/subquery/">subquery</a> that converts the input string &#8216;A1, A2, A4&#8217; to the list of discrete values &#8216;A1&#8217;, &#8216;A2&#8217;, and &#8216;A4&#8217;.</p>
<p>Let&#8217;s examine the solution more closely.</p>
<h3>String to Rows Solution: A Closer Look</h3>
<p>The building blocks of the solution are:</p>
<h4>(1) REGEX_SUBSTR for pattern matching</h4>
<p>REGEXP_SUBSTR is a <a href="https://www.oratable.com/regular-expressions-in-oracle/">regular expression function in Oracle</a> that finds a substring matching a regular expression pattern in a given text.</p>
<p>Syntax:    <br /><font face="Courier New">regex_substr(text, pattern, position, occurrence)</font></p>
<p>where</p>
<ul>
<li><font face="Courier New">text</font>: Source string in which the pattern is to be searched.       <br />&#8216;A1, A2, A3&#8217; in this example.</li>
<li><font face="Courier New">pattern</font>: regular expression that matches the substring to be returned.       <br />[^,]+ in this example, which matches any character sequence in the text that does not contain a comma.</li>
<li><font face="Courier New">position</font>:&#160; Character of <font face="Courier New">text</font> where Oracle should begin the search. In this example, we will begin the search from 1.</li>
<li><font face="Courier New">occurrence</font>:&#160; Nth occurrence of <font face="Courier New">pattern</font> in source string to be returned. </li>
</ul>
<p>So for example, to get the 2nd <font face="Courier New">occurrence</font> of <font face="Courier New">pattern</font> in <font face="Courier New">text</font> which does not contain a comma:</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- REGEXP_SUBSTR to pick the 2nd value from string
SQL&gt; select regexp_substr('A1,A2,A4','&#x5B;^,]+', 1, 2)
  2  from dual;

RE
--
A2
</pre>
<p>To convert the entire string to rows, *all* occurrences of the substrings within commas are to be returned — so we need a way to iterate through 1..last occurrences of substrings in the SQL. That&#8217;s where CONNECT BY comes into play&#8230;</p>
<h4>(2) CONNECT BY for 1..N row generation</h4>
<p>Recursive <a href="https://www.oratable.com/connect-by-hierarchical-query/">CONNECT BY</a> along with <a href="https://www.oratable.com/level-pseudocolumn/">LEVEL</a> can be used as a basic 1..N row generator. </p>
<p>The value returned by level can be used to indicate <font face="Courier New">occurrence</font> in REGEX_SUBSTR.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- 1..N generator using CONNECT BY
SQL&gt; select level
  2  from dual
  3  connect by level &lt;= 4;

     LEVEL
----------
         1
         2
         3
         4</pre>
<p>Putting both together, we get&#8230;</p>
<h4>REGEX_SUBSTR + CONNECT BY for delimiter-separated string —&gt; rows conversion</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- REGEXP_SUBSTR and CONNECT BY to convert string to rows
SQL&gt; select regexp_substr('A1,A2,A4','&#x5B;^,]+', 1, level)
  2  from dual
  3  connect BY regexp_substr('A1,A2,A4', '&#x5B;^,]+', 1, level)
  4  is not null;

REGEXP_SUBSTR('A1,A2,A4'
------------------------
A1
A2
A4
</pre>
<h3>Summary</h3>
<p>A delimiter-separated string can be converted to a set of rows in Oracle SQL, with the combination of the regex function <a href="https://www.oratable.com/regular-expressions-in-oracle/">REGEX_SUBSTR</a> and recursion via <a href="https://www.oratable.com/connect-by-hierarchical-query/">CONNECT BY</a>. This feature can be used for splitting a single input string with comma-separated query parameters, into a list of values.</p>
<p> and </p>
<p>The post <a href="https://www.oratable.com/regex-split-comma-separated-string/">REGEX to Split a Comma-Separated String into Rows</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/regex-split-comma-separated-string/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>How to Install and Configure SQL*Plus Instant Client on Windows</title>
		<link>https://www.oratable.com/sqlplus-instant-client-installation/</link>
					<comments>https://www.oratable.com/sqlplus-instant-client-installation/#comments</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Mon, 06 Aug 2018 14:00:57 +0000</pubDate>
				<category><![CDATA[Miscellaneous]]></category>
		<guid isPermaLink="false">http://www.oratable.com/?p=1482</guid>

					<description><![CDATA[<p>Easy guide on how to install and configure SQL*Plus Instant Client on Windows, and how to use it to connect to remote databases,</p>
<p>The post <a href="https://www.oratable.com/sqlplus-instant-client-installation/">How to Install and Configure SQL*Plus Instant Client on Windows</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/sqlplus-instant-client-installation/" title="Permanent link to How to Install and Configure SQL*Plus Instant Client on Windows"><img loading="lazy" decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2018/08/SQLPlus-Download.jpg" width="480" height="250" alt="Post image for How to Install and Configure SQL*Plus Instant Client on Windows" /></a>
</p><p>GUI tools like TOAD and SQL Developer are all fine, but those who have cut their teeth on SQL*Plus will not do without good old command-line interface. And so, a message like the one below makes one want to set things right immediately&#8230;</p>
<p><span id="more-1482"></span></p>
<p><img loading="lazy" decoding="async" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" border="0" alt="sqlplus not recognized" title="sqlplus not recognized" src="https://www.oratable.com/wp-content/uploads/2018/08/image_thumb.png" width="480" height="118" />Here&#8217;s a step-by-step guide for doing away with this error, by installing SQL*Plus Instant Client on Windows.</p>
<h3>First things first: What is SQL*Plus Instant Client?</h3>
<p>From the Oracle <a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqpug/SQL-Plus-instant-client.html#GUID-9DC272F8-0805-4582-87C6-67B2BC816A2C">documentation</a>:</p>
<blockquote>
<p><em>SQL*Plus Instant Client is a standalone product with all the functionality of SQL*Plus command-line. It connects to existing remote Oracle databases, but does not include its own database. It is easy to install and uses significantly less disk space than the full Oracle Database Client installation required to use SQL*Plus command-line.</em></p>
</blockquote>
<p>If all you want is a simple command-line interface to connect to remote Oracle databases, Instant Client meets your purpose well.</p>
<p>On to the installation&#8230;</p>
<h3>1. Download the installers from OTN: Basic OCI + SQL*Plus</h3>
<p>SQL*Plus Instant Client package needs OCI Instant Client as a pre-requisite. For things to work, the two must be (i) of the same version, and (ii) installed in the same directory. </p>
<p>To download: </p>
<p>Go to OTN&#8217;s <a href="http://www.oracle.com/technetwork/topics/winx64soft-089540.html">Instant Client Downloads for MS Windows</a>. </p>
<p>Accept the license agreement. </p>
<p>Choose the packages you want to install — in this case:</p>
<ul>
<li><em>Basic Light Package</em> &#8211; Smaller version of the Basic OCI package, with only English error messages </li>
<li><em>SQL*Plus Package</em> &#8211; The SQL*Plus command line tool for SQL and PL/SQL queries</li>
</ul>
<p>Versions used: SQL*Plus 12.2.0.1.0, for a Windows 64-bit environment.</p>
<ul></ul>
<p><img loading="lazy" decoding="async" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" border="0" alt="Download SQL*Plus Installers from OTN" title="Download SQL*Plus Installers from OTN" src="https://www.oratable.com/wp-content/uploads/2018/08/image.png" width="480" height="342" /></p>
<p>Click on the hyperlinks to start download — this will download the ZIP files to your local machine.</p>
<p><a href="https://www.oratable.com/wp-content/uploads/2018/08/image-1.png"><img loading="lazy" decoding="async" tyle="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" border="0" title="ZIP files downloaded" alt="ZIP files downloaded" src="https://www.oratable.com/wp-content/uploads/2018/08/image_thumb-1.png" width="234" height="147" /></a></p>
<h3>2. Extract the downloads</h3>
<p>Extract both the ZIPs to the same local folder, say D:\Tools\sqlplus. A new subfolder (instantclient_12_2 in this case) will get created.</p>
<p><img loading="lazy" decoding="async" title="Extract ZIP files" alt="Extract ZIP files"  style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" border="0" src="https://www.oratable.com/wp-content/uploads/2018/08/image-2.png" width="414" height="341" /></p>
<h3>3. Edit the PATH variable to include the SQL*Plus Instant Client directory</h3>
<p>Add the directory containing the Instant Client files to the PATH environment variable. Make sure this addition does not have a conflict with another existing Oracle path — if it does, either remove the existing value or move the new value to the front of the PATH variable.</p>
<p><img loading="lazy" decoding="async" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" border="0" alt="Edit PATH variable" title="Edit PATH variable" src="https://www.oratable.com/wp-content/uploads/2018/08/image-3.png" width="460" height="313" /></p>
<p>We now have the SQL*Plus Instant Client ready to connect to remote databases. </p>
<h3>4. Connect to a Database with SQL*Plus Instant Client via Oracle Net connection identifier</h3>
<p>To connect to a database, first open a command prompt window. </p>
<p>Enter the <a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/netag/configuring-naming-methods.html#GUID-B0437826-43C1-49EC-A94D-B650B6A4A6EE">Easy Connection identifier</a> to connect to the database. For example, the Easy Connection identifier for the HR schema in an XE database, running on host sales-server, port 1521 is:</p>
<p><font face="Courier New">sqlplus hr/your_password@\&quot;//sales-server:1521/XE\&quot;</font></p>
<p>Once entered, SQL*Plus Instant Client will establish the database connection and you will see the SQL prompt.</p>
<p><img loading="lazy" decoding="async" title="SQL*Plus Instant Client Connected to Database" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" border="0" alt="SQL*Plus Instant Client Connected to Database"  src="https://www.oratable.com/wp-content/uploads/2018/08/image-4.png" width="479" height="269" /></p>
<p>You can now run your SQL commands through SQL*Plus Instant Client&#8217;s easy command-line interface.</p>
<p>Other than Easy Connection identifiers, Net Service Names can be used for making database connections via Instant Client. Net Service Names may come from sources such as LDAP or a tnsnames.ora file. For more details on connection options, see the <a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqpug/SQL-Plus-instant-client.html#GUID-A928552E-F454-4989-BA90-7150E53C7C61">user&#8217;s guide and reference</a>. </p>
<h3>Notes:</h3>
<p>Remember that the error in the first screenshot (<em>&#8216;sqlplus&#8217; is not recognized as an internal or external command</em>) could be because of a misconfigured PATH — see step 3. Before you proceed to install SQL*Plus, verify that SQL*Plus is actually absent!</p>
<p>For more on SQL*Plus Instant Client, see the Oracle documentation: <a href="https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqpug/SQL-Plus-instant-client.html#GUID-9DC272F8-0805-4582-87C6-67B2BC816A2C">link</a>. Also check out the advanced configuration tips in this neat tutorial at <a href="http://www.williamrobertson.net/documents/sqlplus-setup.html">williamrobertson.net</a>. </p>
<p>The post <a href="https://www.oratable.com/sqlplus-instant-client-installation/">How to Install and Configure SQL*Plus Instant Client on Windows</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/sqlplus-instant-client-installation/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>DML Error Logging: Supersized DML Operations Made Easy</title>
		<link>https://www.oratable.com/dml-error-logging/</link>
					<comments>https://www.oratable.com/dml-error-logging/#comments</comments>
		
		<dc:creator><![CDATA[oratabler]]></dc:creator>
		<pubDate>Mon, 19 Feb 2018 12:15:44 +0000</pubDate>
				<category><![CDATA[Exceptions]]></category>
		<category><![CDATA[Keywords]]></category>
		<category><![CDATA[Performance]]></category>
		<guid isPermaLink="false">http://www.oratable.com/?p=1394</guid>

					<description><![CDATA[<p>Oracle DML error logging lets you manage very large DML operations with high performance and robust error handling, with the addition of a LOG ERRORS INTO clause with the DML statement. An explanation and working example.</p>
<p>The post <a href="https://www.oratable.com/dml-error-logging/">DML Error Logging: Supersized DML Operations Made Easy</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a class="post_image_link" href="https://www.oratable.com/dml-error-logging/" title="Permanent link to DML Error Logging: Supersized DML Operations Made Easy"><img loading="lazy" decoding="async" class="post_image" src="https://www.oratable.com/wp-content/uploads/2018/02/DML-Error-Logging.jpg" width="480" height="250" alt="DML Error Logging DBMS_ERRLOG" /></a>
</p><p>When you have an enormous load of data to copy/merge from one table to another, you are probably concerned about:</p>
<ul>
<li><strong>Performance:</strong> How fast can the program copy/merge all the data? </li>
<li><strong>Exception handling:</strong> How well does the program deal with errors in the data? Can it highlight bad data if present in the lot, at the same time allow the good data to pass successfully? </li>
</ul>
<p><span id="more-1394"></span></p>
<p>In older versions of Oracle, if you had to insert a million rows using INSERT&#8230;SELECT and even one of those rows had an error, the entire transaction would fail. For selective exception handling, you had no choice but to manage the transaction procedurally.</p>
<p>Oracle 10G R2+ has a way to fortify DML statements with a &quot;LOG ERRORS INTO&quot; clause, which can deal with errors pretty much like a PL/SQL <a href="https://www.oratable.com/sqlcode-and-sqlerrm/">exception handler</a> would. Read onto know how DML error logging works.</p>
<h3>DML Error Logging: Building Blocks</h3>
<p>The LOG ERRORS INTO clause gets suffixed to your regular DML statement&#160; this way:</p>
<p><font size="3" face="Courier New"><font style="background-color: #eeeeee">&lt;DML statement&gt;</font>       <br />LOG ERRORS INTO<font style="background-color: #eeeeee">&lt;error_logging_table&gt;</font> (<font style="background-color: #eeeeee">&lt;tag&gt;</font>)       <br />REJECT LIMIT <font style="background-color: #eeeeee">&lt;reject limit value&gt;</font>;</font></p>
<p>Here&#8217;s a closer look at the components of DML Error Logging.</p>
<h4>1. DML statement</h4>
<p>The DML statement could be an INSERT, UPDATE, <a href="https://www.oratable.com/delete-old-data-from-database/">DELETE</a> or <a href="https://www.oratable.com/oracle-merge-command-for-upsert/">MERGE</a>.</p>
<h4>2. Error logging table</h4>
<p>The error logging table is designed to hold the errorred rows during the DML operation. The error table may be created in any of the two ways:</p>
<ul>
<li><em>Manually</em> via the regular CREATE TABLE command. This table must follow certain guidelines as described in the next section &quot;Error Logging Table Format&quot;. </li>
<li><em>Automatically</em> via call to the DBMS_ERRLOG package:: </li>
</ul>
<blockquote>
<p><font face="Courier New">exec dbms_errlog.create_error_log( &#8216;dml_target_table&#8217;, &#8216;error_logging_table&#8217;);</font> </p>
</blockquote>
<h4>3. Tag</h4>
<p>This is an optional value that goes into each row inserted into the error table. In a batch transaction, it helps to tag both the target DML table rows and the error logging table rows with a batch_id for grouping and cross-validation.</p>
<h4>4. Reject limit value</h4>
<p>An integer that defines the maximum number of errors allowed in the DML statement — if the error count exceeds the reject limit value, the DML statement terminates and all changes made are rolled back.</p>
<p>The default reject limit value is 0.</p>
<p>If you want the DML to go through regardless of the count of errors, set REJECT LIMIT UNLIMITED.</p>
<h3>Error Logging Table Format</h3>
<p>The DML error logging table should follow these specifications.</p>
<h4>1. Mandatory columns</h4>
<p>The table must contain these columns:</p>
<table cellspacing="5" cellpadding="5" width="470" border="0">
<tbody>
<tr>
<td style="font-weight: bold" valign="top" width="103">Column Name</td>
<td style="font-weight: bold" valign="top" width="116">Data Type</td>
<td style="font-weight: bold" valign="top" width="249">Description</td>
</tr>
<tr>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="103">ORA_ERR_NUMBER$</td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="116">NUMBER</td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="249">ORA error number</td>
</tr>
<tr>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #ebf4fa" valign="top" width="103">ORA_ERR_MESG$</td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #ebf4fa" valign="top" width="116">VARCHAR2(2000)</td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #ebf4fa" valign="top" width="249">ORA error message text</td>
</tr>
<tr>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="103">ORA_ERR_ROWID$</td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="116">ROWID </td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="249">Rowid of the row in error, for update and delete; null for insert</td>
</tr>
<tr>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #ebf4fa" valign="top" width="103">ORA_ERR_OPTYP$ </td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #ebf4fa" valign="top" width="116">VARCHAR2(2)</td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #ebf4fa" valign="top" width="249">Type of operation: insert (I), update (U), delete (D). In a <a href="https://www.oratable.com/oracle-merge-command-for-upsert/">MERGE</a>, update rows are marked U, insert rows are marked I.</td>
</tr>
<tr>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="103">ORA_ERR_TAG$</td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="116">VARCHAR2(2000)</td>
<td style="border-top: #dddddd 1px solid; border-right: #dddddd 1px solid; border-bottom: #dddddd 1px solid; border-left: #dddddd 1px solid; background-color: #bbd9ee" valign="top" width="249">Optional tag value from the DML error logging clause</td>
</tr>
</tbody>
</table>
<p style="line-height:0.5em;">&#160;</p>
<p>DBMS_ERRLOG takes care of including the mandatory columns in the error logging table, in the case of automatic error logging table creation.</p>
<p>When creating the error logging table manually, you need to ensure the mandatory columns are present. These columns can be in any order, but they must appear at the start of the table.</p>
<h4>2. Optional columns:</h4>
<p>The error logging table can optionally contain a set of columns to hold data from the row that caused the error. The <strong>column names of the error logging table must match the column names of the DML target table</strong>. If there&#8217;s a mismatch, the column is not logged/value is ignored.</p>
<p>If DBMS_ERRORLOG is used to create the error logging table automatically, all the columns from the DML target table get included in the error logging table. In case of a table with several columns, this may be an unnecessary space overhead: in this scenario it&#8217;s best to create your own error logging table <strong>trimmed to only the essential columns</strong>.</p>
<p>With your own error logging table, you can choose which columns to include: in general, the unique index columns are enough. Position them in the table after all the mandatory columns.</p>
<h4><font color="#111111">3. </font>Error Logging Table Name:</h4>
<p>When creating the error logging table automatically, you can specify its name in the DBMS_ERRLOG parameters.</p>
<p>If the error logging table name is unspecified, it is given a default name of this form:</p>
<p><strong>ERR$_ || first 25 characters of &lt;dml_target_table&gt;</strong> </p>
<p>Example:   <br />dml_target_table name: EMP    <br />error_logging_table name: ERR$_EMP</p>
<p>When creating the error logging table manually, you can give it any name that follows the standard Oracle table naming guidelines. </p>
<p>Let&#8217;s get on with the DML error logging code in action.</p>
<h3>Case Study: Copying huge data volume across tables</h3>
<p>We&#8217;ll build on the problem statement from the <a href="https://www.oratable.com/forall-insert-exception-handling-bulk-dml/">bulk DML with FORALL</a> example:</p>
<p><strong>The tables:</strong></p>
<ul>
<li>PEOPLE — Source table with columns (ID, NAME, STATUS) from which data is to be read. </li>
<li>CUSTOMER — Target table with columns (ID, NAME) to which data is to be inserted.</li>
</ul>
<p>Table CUSTOMER has a <a href="https://www.oratable.com/unique-constraint-vs-unique-index/">unique constraint</a> on columm ID. </p>
<p>Table PEOPLE has no unique constraint, and some data may have IDs repeated.</p>
<p><strong>The requirement:</strong></p>
<p>Copy data from table PEOPLE to CUSTOMER, where the status of the data in PEOPLE is NEW.</p>
<p>Record the errors if they occur during copying. Given the ID repetitions in table PEOPLE, errors are expected due to unique constraint violation in table CUSTOMER.</p>
<p>After copying, update the status column in table PEOPLE: set the successfully copied rows to DONE and the failed rows to ERROR. </p>
<h4>The data before copying:</h4>
<p><pre class="brush: sql; title: ; notranslate">SQL&gt; -- Source DML table PEOPLE
SQL&gt; select * from people;

  ID NAME       STATUS
---- ---------- --------
   1 Jon        NEW
   2 Cersei     NEW
   2 Khal       NEW
   3 Gregor     NEW
   3 Sansa      NEW
   4 Arya       NEW
   5 Eddard     NEW

7 rows selected.

SQL&gt; -- Target DML table CUSTOMER
SQL&gt; select * from customer;

no rows selected
</pre>
</p>
<h3>DML Error Logging: Solution Approach</h3>
<p>1. Create an <strong>error logging table</strong>.</p>
<p>2. <strong>INSERT INTO CUSTOMER</strong>&#8230;SELECT FROM PEOPLE, logging errors into the error logging table.</p>
<p>3. <strong>UPDATE PEOPLE status</strong> from NEW to DONE/ERROR conditionally with a <a href="https://www.oratable.com/simple-case-searched-case/">searched case</a>.</p>
<p>You might ask why we didn&#8217;t consider <strong>direct-path INSERT</strong> (that is, INSERT with <a href="https://oracle-base.com/articles/misc/append-hint">/*+ APPEND */</a> hint), which could go much faster for high data volumes than <strong>conventional-path INSERT</strong>.</p>
<p>The reason is that with DML error logging,  direct-path INSERT will fail when a unique constraint violation occurs, while a conventional-path INSERT will log these errors to the error logging table. Our expected error in this case is the unique constraint violation on table CUSTOMER, so we need to stick to direct-path INSERT&#8230;</p>
<p>&#8230;unless we are game to try this <a href="https://it.toolbox.com/blogs/ganotedp/tricking-or-tripping-dml-error-logging-in-oracle-012514">clever hack</a>.</p>
<h3>DML Error Logging in Action</h3>
<h4>[BEFORE] Data:</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Source DML table PEOPLE
SQL&gt; select * from people;

  ID NAME       STATUS
---- ---------- --------
   1 Jon        NEW
   2 Cersei     NEW
   2 Khal       NEW
   3 Gregor     NEW
   3 Sansa      NEW
   4 Arya       NEW
   5 Eddard     NEW

7 rows selected.

SQL&gt; -- Target DML table CUSTOMER
SQL&gt; select * from customer;

no rows selected
</pre>
<p>Note that of the seven rows of source data, two IDs (2 and 3) are repeated — this means the INSERT should work for only five of the source rows, and two rows should end up in error.</p>
<h4>1. Create the error logging table for DML target table</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Create the error logging table for DML target table
SQL&gt; exec dbms_errlog.create_error_log( 'CUSTOMER' );

PL/SQL procedure successfully completed.
</pre>
<p>Since we used the defaults for DBMS_ERRLOG, the error logging table would be named ERR$_CUSTOMER.</p>
<h4>2. Verify the structure of the error logging table </h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Verify the structure of the automatically
SQL&gt; -- created error_logging_table for CUSTOMER
SQL&gt; desc err$_customer
 Name                    Null?    Type
 ----------------------- -------- ---------------
 ORA_ERR_NUMBER$                  NUMBER
 ORA_ERR_MESG$                    VARCHAR2(2000)
 ORA_ERR_ROWID$                   ROWID
 ORA_ERR_OPTYP$                   VARCHAR2(2)
 ORA_ERR_TAG$                     VARCHAR2(2000)
 ID                               VARCHAR2(4000)
 NAME                             VARCHAR2(4000)
 </pre>
<p>We see that the table has the mandatory columns ending in $, and the other columns (ID, NAME) from the DML target table CUSTOMER.</p>
<h4>3. Run the script with DML Error Logging</h4>
<p>This script inserts into CUSTOMER via select from PEOPLE where status = NEW. It then updates PEOPLE status to DONE or ERROR, depending on whether a corresponding row exists in the error logging table or not.</p>
<pre class="brush: sql; title: ; notranslate">SQL&gt; declare
  2    -- Id for the run. Not really necessary in this example
  3    -- but useful if DML error logging is part of a stored
  4    -- proc and called periodically via batch program
  5    batch_id  number := 999;
  6  begin
  7
  8    -- Insert into CUSTOMER, logging errors along the way
  9    insert into customer (id, name)
 10      select p.id, p.name
 11      from people p
 12      where status = 'NEW'
 13    log errors into err$_customer (batch_id)
 14    reject limit unlimited;
 15
 16    -- Update status in PEOPLE after copying data
 17    -- DONE if successful, ERROR if failed
 18    update people p
 19    set p.status = case when exists
 20      (select 1
 21       from err$_customer e
 22       -- identify the copied row
 23       where      p.id = e.id and p.name = e.name
 24       and ora_err_tag$ = batch_id)
 25       then 'ERROR'
 26       else 'DONE'
 27       end
 28    where status = 'NEW';
 29  end;
 30  /

PL/SQL procedure successfully completed.
</pre>
<p>The script ran successfully. Let&#8217;s check the errors if any in ERR$_CUSTOMER, and the state of the data in the two tables PEOPLE and CUSTOMER.</p>
<h4>4. Check failed rows in the error_logging_table</h4>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Values in the error_logging_table
SQL&gt; select ora_err_number$ number$
  2       , ora_err_mesg$ mesg$
  3       , ora_err_optyp$ optyp$
  4       , ora_err_tag$ tag$
  5       , id
  6       , name
  7  from   err$_customer;

NUMBER$ MESG$                        OPTYP$ TAG$ ID NAME
------- ---------------------------- ------ ---- -- -----
      1 ORA-00001: unique constraint I      999  2  Khal
         (HR.CUSTOMER_UK) violated

      1 ORA-00001: unique constraint I      999  3  Sansa
         (HR.CUSTOMER_UK) violated
</pre>
<p>As expected, two rows have failed&#160; due to unique constraint violation. </p>
<h4>[AFTER] Data:</h4>
<ul>
<li>CUSTOMER has 5 new unique rows</li>
<li>ERR$_CUSTOMER has 2 error rows with error details</li>
<li>PEOPLE status correctly indicates which rows were copied successfully, which were not (five DONE, two ERROR)</li>
</ul>
<pre class="brush: sql; title: ; notranslate">SQL&gt; -- Source DML table PEOPLE
SQL&gt; select * from people;

  ID NAME       STATUS
---- ---------- --------
   1 Jon        DONE
   2 Cersei     DONE
   2 Khal       ERROR
   3 Gregor     DONE
   3 Sansa      ERROR
   4 Arya       DONE
   5 Eddard     DONE

7 rows selected.

SQL&gt;
SQL&gt; -- Target DML table CUSTOMER
SQL&gt; select * from customer;

  ID NAME
---- ----------
   1 Jon
   2 Cersei
   3 Gregor
   4 Arya
   5 Eddard
</pre>
<h3>Which to Use: DML Error Logging or FORALL&#8230;SAVE EXCEPTIONS?</h3>
<p>When both DML Error Logging and <a href="https://www.oratable.com/forall-insert-exception-handling-bulk-dml/">FORALL&#8230;SAVE EXCEPTIONS</a> give you performance/exception handling benefits for transactions with large data volume, which is the preferred approach?</p>
<p>The question doesn&#8217;t have an all-encompassing &#8220;this approach&#8221; answer &#8211; if it did, Oracle would probably not have two ways of doing the same thing. You would need to analyse your requirements/benchmark for both approaches. </p>
<p><strong>DML Error Logging</strong> can be <strong>very fast if you can use direct-path load</strong> (INSERT /*+ APPEND */) and if there are no indexes on the target table. If you must use conventional-path load (as in the cases study above), you may want to evaluate the approach more carefully.</p>
<p>FORALL&#8230;SAVE EXCEPTIONS is faster than row-by-row processing, but might not be as fast as direct-path load with DML Error Logging.</p>
<p>Even if you can use direct-path load, there are factors other than speed to consider:</p>
<ul>
<li><strong>Locking:</strong> direct-path load locks the object while it&#8217;s in action; bulk DML does not.</li>
<li><strong>Free space utilization: </strong>direct-path load does not utilise existing free space, which bulk DML does. </li>
<li><strong>Transaction management:</strong> direct-path load needs a commit/rollback right&#160; after completion*; bulk DML does not and can support further processing in the same transaction. </li>
</ul>
<p>*Example: try selecting from a table immediately after direct-path load, without commit/rollback. You get:   </p>
<blockquote><p>ORA-12838: cannot read/modify an object after modifying it in parallel</p></blockquote>
<h3>Summary</h3>
<p>This article explains the features of DML Error Logging, with a working example. It also gives guidelines for making a choice between DML Error Logging and bulk DML with SAVE EXCEPTIONS.</p>
<h3>For Further Reading</h3>
<ul>
<li><a href="http://asktom.oracle.com/Misc/how-cool-is-this.html" how-cool-is-this.html?="how-cool-is-this.html?" Misc="Misc" asktom.oracle.com="asktom.oracle.com" http:="http:">AskTom: How cool is this</a> </li>
<li><a href="https://docs.oracle.com/database/121/ARPLS/d_errlog.htm">Oracle documentation</a> </li>
</ul>
<p>The post <a href="https://www.oratable.com/dml-error-logging/">DML Error Logging: Supersized DML Operations Made Easy</a> appeared first on <a href="https://www.oratable.com">Oratable</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.oratable.com/dml-error-logging/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
