{"id":73142,"date":"2015-11-20T14:46:57","date_gmt":"2015-11-20T14:46:57","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/uncategorized\/about-functions-records-and-result-cache\/"},"modified":"2021-07-14T13:07:19","modified_gmt":"2021-07-14T13:07:19","slug":"about-functions-records-and-result-cache","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/oracle-databases\/about-functions-records-and-result-cache\/","title":{"rendered":"About functions, records and result cache"},"content":{"rendered":"<p>This article is about functions and how to get more out of them if you combine them with record structures.<\/p>\n<p>Functions are the instrument in the Oracle Database to execute code and return a value.\u00a0Nothing new here, everybody who develops in PL\/SQL on the database knows that.<\/p>\n<p>In case you&#8217;re just starting with Oracle and want to know more about procedures and functions, check out this post by David Njoku: &#8220;<a href=\"https:\/\/allthingsoracle.com\/oracle-for-absolute-beginners-part-6-procedures-and-functions\/\" target=\"_blank\">Oracle for Absolute Beginners: Part 6 \u2013 Procedures and Functions<\/a>&#8220;.<\/p>\n<h2>Functions returning only\u00a0one\u00a0value<\/h2>\n<p>Let&#8217;s start with the easy stuff&#8230; the following function will retrieve a record from a table and concatenate the first and last name.<\/p>\n<pre>CREATE OR REPLACE FUNCTION get_full_name(p_employee_id IN hr.employees.employee_id%TYPE)\r\nRETURN VARCHAR2\r\nIS\r\n   CURSOR c_employees\r\n       IS\r\n   SELECT first_name\r\n        , last_name\r\n     FROM hr.employees\r\n    WHERE employee_id = p_employee_id;\r\n\r\n   r_employees c_employees%ROWTYPE;\r\n \r\nBEGIN\r\n   OPEN c_employees;\r\n   FETCH c_employees INTO r_employees;\r\n   CLOSE c_employees;\r\n \r\n   RETURN r_employees.first_name ||' '||r_employees.last_name;\r\nEND;\r\n\/\r\n<\/pre>\n<pre>BEGIN\r\n   dbms_output.put_line(get_full_name(100));\r\nEND;\r\n\/\r\n\r\nSteven King\r\n<\/pre>\n<p>Nothing fancy, just fetching one record from the table and returning the concatenated values. The simplest form of a function.<\/p>\n<h2>Functions returning multiple values<\/h2>\n<p>But if I need a function that returns multiple values, how can I do that?<\/p>\n<p>You can add an OUT(or IN\/OUT) parameter in the functions parameter list.<\/p>\n<pre>CREATE OR REPLACE FUNCTION get_full_name( p_employee_id IN hr.employees.employee_id%TYPE \r\n                                        , p_first_name OUT hr.employees.first_name%TYPE \r\n                                        , p_last_name OUT hr.employees.last_name%TYPE \r\n                                        )\r\nRETURN VARCHAR2\r\nIS\r\n   CURSOR c_employees\r\n       IS\r\n   SELECT first_name\r\n        , last_name\r\n     FROM hr.employees\r\n    WHERE employee_id = p_employee_id;\r\n\r\n   r_employees c_employees%ROWTYPE;\r\n\r\nBEGIN\r\n\r\n   OPEN c_employees;\r\n   FETCH c_employees INTO r_employees;\r\n   CLOSE c_employees;\r\n\r\n   p_first_name := r_employees.first_name;\r\n   p_last_name := r_employees.last_name;\r\n\r\n   RETURN r_employees.first_name ||' '||r_employees.last_name;\r\nEND;\r\n\/\r\n<\/pre>\n<pre>DECLARE\r\n   first_name hr.employees.first_name%TYPE;\r\n   last_name hr.employees.last_name%TYPE;\r\nBEGIN\r\n\r\n   dbms_output.put_line(get_full_name(100, first_name, last_name));\r\n   dbms_output.put_line(first_name);\r\n   dbms_output.put_line(last_name);\r\n   \r\nEND;\r\n\/\r\n\r\nSteven King\r\nSteven\r\nKing\r\n<\/pre>\n<p>This was just an example, you shouldn&#8217;t create a function with OUT or IN\/OUT parameters!<\/p>\n<p>In the following article Steven Feuerstein gives you some arguments: &#8220;<a href=\"http:\/\/www.oracle.com\/technetwork\/issue-archive\/2007\/07-jul\/o47plsql-097129.html\" target=\"_blank\">Do not put OUT or IN OUT arguments into the parameter list of a function<\/a>&#8220;.\u00a0I agree on that one. A function is created to RETURN a value, use it that way.<\/p>\n<p>We have to return multiple variables in one RETURN statement. We can achieve this by using <a href=\"https:\/\/docs.oracle.com\/database\/121\/LNPLS\/record_definition.htm#LNPLS01338\" target=\"_blank\">record variables<\/a>.<\/p>\n<p>So&#8230; let us agree that we return a record type for returning multiple output parameters instead of using OUT parameters in the function.\u00a0In this case, to make it easy, I will use a record structure based on the structure of a table (%ROWTYPE).<\/p>\n<pre>CREATE OR REPLACE FUNCTION get_employee(p_employee_id IN hr.employees.employee_id%TYPE)\r\nRETURN employees%ROWTYPE\r\nIS\r\n\r\n   CURSOR c_employees\r\n       IS\r\n   SELECT *\r\n     FROM hr.employees\r\n    WHERE employee_id = p_employee_id;    \r\n\r\n   r_employees c_employees%ROWTYPE;\r\n   \r\nBEGIN\r\n\r\n   OPEN c_employees;\r\n   FETCH c_employees INTO r_employees;\r\n   CLOSE c_employees;\r\n   \r\n   RETURN r_employees;\r\n\r\nEND;\r\n\/\r\n<\/pre>\n<p>Now, we have all the values we need in different record fields and we can format the way we want to.<\/p>\n<pre>DECLARE\r\n\r\n   r_employee employees%ROWTYPE;\r\n\r\nBEGIN\r\n   r_employee := get_employee(100);\r\n   dbms_output.put_line(r_employee.last_name ||', '||r_employee.first_name);\r\n\r\nEND;\r\n\/\r\n\r\nKing, Steven\r\n<\/pre>\n<h2>Functions returning a record structure, but only needing 1 value<\/h2>\n<p>&#8220;That record structure is nice, but I only need 1 value&#8221;.\u00a0When I use a record structure, I first need to assign the RETURN value to a local variable and then use the local variable.\u00a0A little bit of overhead?<\/p>\n<p>What a lot of people don&#8217;t know, is that you don&#8217;t have to assign the record to a local variable, you can use the fields of the record directly in your function.\u00a0Say what?\u00a0Yes, this is possible:<\/p>\n<pre>BEGIN\r\n   dbms_output.put_line(get_employee(100).last_name);\r\nEND;\r\n\/\r\n\r\nKing\r\n<\/pre>\n<p>When you\u00a0only need one variable from a function returning a record, you can use &#8220;function_name().field_name&#8221;. Interesting, isn&#8217;t it?<\/p>\n<p>Of course you can also do this:<\/p>\n<pre>BEGIN\r\n   dbms_output.put_line(get_employee(100).last_name);\r\n   dbms_output.put_line(get_employee(100).first_name);\r\n   dbms_output.put_line(get_employee(100).last_name ||', '||get_employee(100).first_name);\r\n\r\nEND;\r\n\/\r\n\r\nKing\r\nSteven\r\nKing, Steven\r\n<\/pre>\n<p>Indeed, no need to declare a variable based on the record structure.\u00a0But there&#8217;s a disadvantage! The function is executed several times\u00a0in the previous example.\u00a0We don&#8217;t want that!<\/p>\n<h2>Functions returning a record structure combined with Result Cache<\/h2>\n<p>One of Oracle&#8217;s caching mechanisms is &#8220;Function Result Cache&#8221;. This means that Oracle caches the result of the function and when there&#8217;s a new call to the function with the same input, it returns the result that is stored in the cache.\u00a0Wouldn&#8217;t it be cool if we could use that?<\/p>\n<p>A little test case. I&#8217;ll write a line using dbms_output.put_line every time I enter the function. Now we can see how many times the function will be executed.<\/p>\n<pre>CREATE OR REPLACE FUNCTION get_employee(p_employee_id IN hr.employees.employee_id%TYPE)\r\nRETURN employees%ROWTYPE\r\nIS\r\n\r\n   CURSOR c_employees\r\n       IS\r\n   SELECT *\r\n     FROM hr.employees\r\n    WHERE employee_id = p_employee_id;    \r\n\r\n   r_employees c_employees%ROWTYPE;\r\n   \r\nBEGIN\r\n\r\n   dbms_output.put_line('execute get_employee');\r\n\r\n   OPEN c_employees;\r\n   FETCH c_employees INTO r_employees;\r\n   CLOSE c_employees;\r\n   \r\n   RETURN r_employees;\r\n\r\nEND;\r\n\/\r\n<\/pre>\n<p>And execute this block of code again:<\/p>\n<pre>BEGIN\r\n\r\n   dbms_output.put_line(get_employee(100).last_name ||', '||get_employee(100).first_name);\r\n\r\nEND;\r\n\/\r\n\r\nexecute get_employee\r\nexecute get_employee\r\nKing, Steven\r\n<\/pre>\n<p>As you can see, the function is executed twice.<\/p>\n<p>Now we add the result cache:<\/p>\n<pre>CREATE OR REPLACE FUNCTION get_employee(p_employee_id IN hr.employees.employee_id%TYPE)\r\nRETURN employees%ROWTYPE RESULT_CACHE\r\nIS\r\n\r\n   CURSOR c_employees\r\n       IS\r\n   SELECT *\r\n     FROM hr.employees\r\n    WHERE employee_id = p_employee_id;    \r\n\r\n   r_employees c_employees%ROWTYPE;\r\n   \r\nBEGIN\r\n\r\n   dbms_output.put_line('execute get_employee');\r\n\r\n   OPEN c_employees;\r\n   FETCH c_employees INTO r_employees;\r\n   CLOSE c_employees;\r\n   \r\n   RETURN r_employees;\r\n\r\nEND;\r\n\/\r\n<\/pre>\n<pre>BEGIN\r\n\r\n   dbms_output.put_line(get_employee(100).last_name ||', '||get_employee(100).first_name);\r\n\r\nEND;\r\n\/\r\n\r\n\r\nexecute get_employee\r\nKing, Steven\r\n<\/pre>\n<p>As you can see the function is executed only once!<\/p>\n<h2>Conclusion<\/h2>\n<p>We created a nice API to get information from a table that is flexible(we can get one or more fields from a table) and that uses a caching mechanism to gain performance(the query isn&#8217;t executed every time I need the same information).<\/p>\n<p>This will certainly contribute to the maintainability of your application.<\/p>\n<h2>But&#8230;<\/h2>\n<p>There&#8217;s a little downside in using record structures in a function &#8211; you can&#8217;t use it in SQL. Not even &#8220;get_employee(100).last_name&#8221; will work in SQL.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article is about functions and how to get more out of them if you combine them with record structures. Functions are the instrument in the Oracle Database to execute code and return a value.\u00a0Nothing new here, everybody who develops in PL\/SQL on the database knows that. In case you&#8217;re just starting with Oracle and want to know more about&hellip;<\/p>\n","protected":false},"author":316191,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143533],"tags":[4658],"coauthors":[],"class_list":["post-73142","post","type-post","status-publish","format-standard","hentry","category-oracle-databases","tag-functions"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73142","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/316191"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=73142"}],"version-history":[{"count":1,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73142\/revisions"}],"predecessor-version":[{"id":91638,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73142\/revisions\/91638"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=73142"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=73142"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=73142"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=73142"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}