{"id":1243,"date":"2020-03-11T18:13:26","date_gmt":"2020-03-11T16:13:26","guid":{"rendered":"https:\/\/blog.sqlora.com\/en\/?p=1243"},"modified":"2024-10-09T13:56:44","modified_gmt":"2024-10-09T11:56:44","slug":"parameterized-views-in-oracle-no-problem-with-sql-macros","status":"publish","type":"post","link":"https:\/\/blog.sqlora.com\/en\/parameterized-views-in-oracle-no-problem-with-sql-macros\/","title":{"rendered":"Parameterized Views in Oracle? No problem! With SQL macros!"},"content":{"rendered":"\n<p>Views have always been an efficient tool for encapsulating complex logic, creating defined access structures and so on. But there is one thing views cannot do: accept parameters. And this is unfortunately a big disadvantage in terms of flexibility. As a result, there are a number of workarounds, none of them without their drawbacks.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p><\/p>\n\n\n\n<p>I will use a little bit complicated example but the one that have a real practical application in the domain of data warehousing and ETL. It&#8217;s about historizing all changes according to slowly changing dimensions type II principle. My colleague <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/www.salvis.com\/blog\/\" target=\"_blank\">Philipp Savlisberg<\/a> has posted a historized  version of the famous SCOTT schema to look at <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/www.salvis.com\/blog\/2012\/12\/24\/joining-temporal-intervals\/\" target=\"_blank\">joining temporal intervals<\/a>. He also <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/00_setup_data.sql_.txt\" target=\"_blank\">provides a script<\/a> to create this schema and data.<\/p>\n\n\n\n<p>To simplify the queries, two types of the views are often placed on top of the versioned tables: the one to query a version of the data at any point in time and the other to query all versions within a time interval.<\/p>\n\n\n\n<p>Let&#8217;s look at the data for one employee (EMPNO=7788): <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; SELECT empvid, empno, valid_from, valid_to, ename, sal \nFROM empv \nWHERE empno = 7788\n\n    EMPVID      EMPNO VALID_FROM VALID_TO  ENAME           SAL\n---------- ---------- ---------- --------- -------- ----------\n         8       7788 19-APR-87  31-DEC-89 SCOTT          3000\n        22       7788 01-JAN-90  31-MAR-91 Scott          3000\n        36       7788 01-APR-91  31-DEC-99 Scott          3300\n\n3 rows selected.\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 1: keeping all changes in a historized EMP table<\/em><\/strong><\/p>\n\n\n\n<p> Would be great, if we could get a view with the following query: <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSELECT * \nFROM   empv \nWHERE  p_eff_date BETWEEN valid_from  AND valid_to\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 2: the desired query for the view <\/em><\/strong><\/p>\n\n\n\n<p>From my point of view, the best known workaround is that a view is defined using SYS_CONTEXT calls instead of the desired parameters. You have to create a user context and set the variables your view should use before querying the view. That alone is quite inconvenient. But another drawback is that you are always using bind variables then. Yes, I said that! Bind variables are good most of the time but not always. I&#8217;m doing a lot of ETL and I would often prefer literals over binds. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- create package and context \n...\n-- create view\nCREATE VIEW emp_hist_v AS \nSELECT * \nFROM   empv\nWHERE  SYS_CONTEXT(&#039;MY_VIEW_CONTEXT&#039;,&#039;EFF_DATE&#039;)\n       BETWEEN valid_from AND valid_to;\n       \n-- Set the context variable\n...        \n\n-- Query is now possible \nSELECT * FROM emp_hist_view;\n<\/pre><\/div>\n\n\n<p> <strong><em>Listing 3: simplified description of SYS_CONTEXT workaround<\/em><\/strong><\/p>\n\n\n\n<p>Another one is to use a package and package variables as parameters. Define the setter and getter methods and use the getter methods instead of parameters in your view.  Well, the drawbacks are the same: you have to set the variables first and you cannot switch to literals too.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE OR REPLACE PACKAGE emp_hist_pkg IS\n\ng_eff_date date;\n\nPROCEDURE set_eff_date(p_date IN DATE);\nFUNCTION get_eff_date RETURN DATE DETERMINISTIC PARALLEL_ENABLE;\n\nEND;\n\/ \n\nCREATE VIEW emp_hist_v AS \nSELECT * \nFROM   empv\nWHERE  emp_hist_pkg.get_eff_date BETWEEN valid_from AND valid_to;\n\n--  set parameter\nBEGIN \n   emp_hist_pkg.set_eff_date(DATE &#039;1990-01-01&#039;);\nEND;   \n\n-- Query is now possible \nSELECT * FROM emp_hist_view;\n\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 4: simplified description of a workaround with package variables<\/em><\/strong><\/p>\n\n\n\n<p>Yet another popular option is not having a view at all, but rather to query a pipelined function which acts as view and can accept parameter. But again, mixing SQL and PL\/SQL means you have to keep context switches in mind.<\/p>\n\n\n\n<p>Okay, you might ask, what&#8217;s the problem here? Just query the view with the desired parameters as WHERE predicates and they will hopefully be pushed down into the view query. It may be or maybe not, depending of how complex your view is. And, first of all, you have to expose those columns from the view to be able to filter on them. What if your view has a scalar subquery and that&#8217;s where you want to introduce a parameter? How simple would it be just to return another column from a scalar subquery? Not at all! Maybe you have to rewrite the whole query because of that.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">SQL Macros <\/h4>\n\n\n\n<p>Let&#8217;s again look at the query, we would like to implemet as a view: <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSELECT * \nFROM   empv \nWHERE  p_eff_date BETWEEN valid_from  AND valid_to\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 5: <\/em><\/strong> <strong><em>the desired query for the view<\/em><\/strong> <\/p>\n\n\n<script async src=\"\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js?client=ca-pub-2925154690547867\" crossorigin=\"anonymous\"><\/script><ins class=\"adsbygoogle\" style=\"display:block; text-align:center;\" data-ad-client=\"ca-pub-2925154690547867\" \ndata-ad-slot=\"2727403138\" \ndata-ad-layout=\"in-article\"\ndata-ad-format=\"fluid\"><\/ins>\n<script> \n(adsbygoogle = window.adsbygoogle || []).push({}); \n<\/script>\n\n\n\n<p>Well, we know, with a plain view it is just not possible. How to solve the task with a SQL macro? First, we need a table macro (default), because we will use it in the FROM clause.  Then just take the query from above and put it into a SQL macro function:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE OR REPLACE FUNCTION emp_view (p_eff_date date) \nRETURN VARCHAR2 SQL_MACRO IS\nv_query varchar2(500);\nBEGIN\n  v_query := q&#039;!SELECT * \n                FROM empv \n                WHERE  p_eff_date between valid_from  AND valid_to!&#039;;\n  RETURN v_query;\nEND;\n\/\n\nSQL&gt; SELECT empvid, empno, valid_from, valid_to, ename, sal  \nFROM   emp_view(DATE &#039;1990-02-01&#039;) \nWHERE  empno = 7788\n\n    EMPVID      EMPNO VALID_FROM VALID_TO  ENAME           SAL\n---------- ---------- ---------- --------- -------- ----------\n        22       7788 01-JAN-90  31-MAR-91 Scott          3000\n1 row selected.\n\nDECLARE\n  l_clob CLOB;\nBEGIN\n  DBMS_UTILITY.expand_sql_text (\n    input_sql_text  =&gt; q&#039;!SELECT * \nFROM   emp_view(DATE &#039;1990-02-01&#039;) \nWHERE  empno = 7788!&#039;,\n    output_sql_text =&gt; l_clob  );\n  DBMS_OUTPUT.put_line(l_clob);\nEND;\n\/\n\nSELECT &quot;A1&quot;.&quot;EMPVID&quot; &quot;EMPVID&quot;,&quot;A1&quot;.&quot;EMPNO&quot; &quot;EMPNO&quot;,&quot;A1&quot;.&quot;ENAME&quot; &quot;ENAME&quot;,&quot;A1&quot;.&quot;JOBNO&quot; &quot;JOBNO&quot;,&quot;A1&quot;.&quot;MGR&quot; &quot;MGR&quot;,&quot;A1&quot;.&quot;HIREDATE&quot; &quot;HIREDATE&quot;,&quot;A1&quot;.&quot;SAL&quot; &quot;SAL&quot;,&quot;A1&quot;.&quot;COMM&quot; &quot;COMM&quot;,&quot;A1&quot;.&quot;DEPTNO&quot; &quot;DEPTNO&quot;,&quot;A1&quot;.&quot;VALID_FROM&quot; &quot;VALID_FROM&quot;,&quot;A1&quot;.&quot;VALID_TO&quot; &quot;VALID_TO&quot; \nFROM  (SELECT &quot;A2&quot;.&quot;EMPVID&quot; &quot;EMPVID&quot;,&quot;A2&quot;.&quot;EMPNO&quot; &quot;EMPNO&quot;,&quot;A2&quot;.&quot;ENAME&quot; &quot;ENAME&quot;,&quot;A2&quot;.&quot;JOBNO&quot; &quot;JOBNO&quot;,&quot;A2&quot;.&quot;MGR&quot; &quot;MGR&quot;,&quot;A2&quot;.&quot;HIREDATE&quot; &quot;HIREDATE&quot;,&quot;A2&quot;.&quot;SAL&quot; &quot;SAL&quot;,&quot;A2&quot;.&quot;COMM&quot; &quot;COMM&quot;,&quot;A2&quot;.&quot;DEPTNO&quot; &quot;DEPTNO&quot;,&quot;A2&quot;.&quot;VALID_FROM&quot; &quot;VALID_FROM&quot;,&quot;A2&quot;.&quot;VALID_TO&quot; &quot;VALID_TO&quot; \nFROM  (SELECT &quot;A3&quot;.&quot;EMPVID&quot; &quot;EMPVID&quot;,&quot;A3&quot;.&quot;EMPNO&quot; &quot;EMPNO&quot;,&quot;A3&quot;.&quot;ENAME&quot; &quot;ENAME&quot;,&quot;A3&quot;.&quot;JOBNO&quot; &quot;JOBNO&quot;,&quot;A3&quot;.&quot;MGR&quot; &quot;MGR&quot;,&quot;A3&quot;.&quot;HIREDATE&quot; &quot;HIREDATE&quot;,&quot;A3&quot;.&quot;SAL&quot; &quot;SAL&quot;,&quot;A3&quot;.&quot;COMM&quot; &quot;COMM&quot;,&quot;A3&quot;.&quot;DEPTNO&quot; &quot;DEPTNO&quot;,&quot;A3&quot;.&quot;VALID_FROM&quot; &quot;VALID_FROM&quot;,&quot;A3&quot;.&quot;VALID_TO&quot; &quot;VALID_TO&quot; \nFROM &quot;ONFTEST&quot;.&quot;EMPV&quot; &quot;A3&quot; \nWHERE TO_DATE(&#039; 1990-02-01 00:00:00&#039;, &#039;syyyy-mm-dd hh24:mi:ss&#039;)&gt;=&quot;A3&quot;.&quot;VALID_FROM&quot; AND \nTO_DATE(&#039; 1990-02-01 00:00:00&#039;, &#039;syyyy-mm-dd hh24:mi:ss&#039;)&lt;=&quot;A3&quot;.&quot;VALID_TO&quot;) &quot;A2&quot;) &quot;A1&quot; WHERE &quot;A1&quot;.&quot;EMPNO&quot;=7788\n\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 6: create and query SQL macro<\/em><\/strong><\/p>\n\n\n\n<p>That&#8217;s it! It looks like we were quering a function, just like in a workaround with pipelined functions, but the difference is huge. The function will run only at parse time and two substitutions are made then. First, the parameter reference will be substituted in the return string of a function. Second, the return string of the function (now with a literal instead of a parameter) will be substitued in the query text instead of a function call. Using DBMS_UTILITY.expand_sql_text, we can see the query actually running. No context switch at all!<\/p>\n\n\n\n<p>But the requirement is actually generic. We need views for all versioned tables. Do we have to create a SQL macro function for every table? No! Being able to pass a table parameter into a SQL macro function, we can just create very simple interface for querieng those tables. With function overloading capabilities of PL\/SQL we can even use the &#8220;same&#8221; function for both scenarios, point in time and interval. See implementation of the package TIME_MACHINE  in Listing 7. I&#8217;ve borrowed this &#8220;time machnine&#8221; analogy from my colleague <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/danischnider.wordpress.com\/\" target=\"_blank\">Dani Schnider<\/a>, but his presentation is only available <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"http:\/\/www.slideshare.net\/trivadis\/die-generierte-zeitmaschine-historisierung-auf-knopfdruck\" target=\"_blank\">in german.<\/a> <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE OR REPLACE PACKAGE time_machine IS\n-- point in time\nFUNCTION vw (p_tab DBMS_TF.Table_t,  p_eff_date date) \nRETURN VARCHAR2 SQL_MACRO;\n--interval\nFUNCTION vw (p_tab DBMS_TF.Table_t,  p_eff_date date, p_exp_date date) \nRETURN VARCHAR2 SQL_MACRO;\n\nEND time_machine;\n\nCREATE OR REPLACE PACKAGE BODY time_machine IS\n\nFUNCTION vw (p_tab DBMS_TF.Table_t,  p_eff_date date) \nRETURN VARCHAR2 SQL_MACRO\nIS\nv_query varchar2(500);\nBEGIN\n  v_query := q&#039;!SELECT * \n                FROM   p_tab \n                WHERE  p_eff_date BETWEEN valid_from  AND valid_to!&#039;;\n  RETURN v_query;\nEND;\n\nFUNCTION vw (p_tab DBMS_TF.Table_t,  p_eff_date date, p_exp_date date) \nRETURN VARCHAR2 SQL_MACRO IS\nv_query varchar2(500);\nBEGIN\n  v_query := q&#039;!SELECT * \n                FROM   p_tab \n                WHERE  valid_from &lt;= p_exp_date \n                AND    valid_to &gt;= p_eff_date!&#039;;\n  RETURN v_query;\nEND;\n\nEND time_machine;\n\/\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 7: creating the time machine<\/em><\/strong> <strong><em>interface<\/em><\/strong><\/p>\n\n\n\n<p>This is how we can use the &#8220;time machine&#8221; to get both point in time as well as interval data from EMPV and DEPTV tables:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; SELECT empvid, empno, valid_from, valid_to, ename, sal \nFROM   time_machine.vw(empv, DATE &#039;1990-02-01&#039;) \nWHERE  empno = 7788\n\n    EMPVID      EMPNO VALID_FROM VALID_TO  ENAME           SAL\n---------- ---------- ---------- --------- -------- ----------\n        22       7788 01-JAN-90  31-MAR-91 Scott          3000\n1 row selected.\n\nSQL&gt; SELECT empvid, empno, valid_from, valid_to, ename, sal \nFROM   time_machine.vw(empv, DATE &#039;1990-01-01&#039;, DATE &#039;2020-01-01&#039;) \nWHERE  empno = 7788\n\n    EMPVID      EMPNO VALID_FROM VALID_TO  ENAME           SAL\n---------- ---------- ---------- --------- -------- ----------\n        22       7788 01-JAN-90  31-MAR-91 Scott          3000\n        36       7788 01-APR-91  31-DEC-99 Scott          3300\n\n2 rows selected.\n\nSQL&gt; SELECT * \nFROM   time_machine.vw(deptv, DATE &#039;1990-02-01&#039;)\n\n   DEPTVID     DEPTNO DNAME          LOC           VALID_FROM VALID_TO \n---------- ---------- -------------- ------------- ---------- ---------\n         1         10 ACCOUNTING     NEW YORK      01-JAN-80  28-FEB-90\n         2         20 RESEARCH       DALLAS        01-JAN-80  28-FEB-90\n         3         30 SALES          CHICAGO       01-JAN-80  28-FEB-90\n         4         40 OPERATIONS     BOSTON        01-JAN-80  28-FEB-90\n\n4 rows selected.\n\nSQL&gt; SELECT * \nFROM   time_machine.vw(deptv, DATE &#039;1980-02-01&#039;, DATE &#039;2020-01-01&#039;)\nWHERE DEPTNO = 10\n\n   DEPTVID     DEPTNO DNAME          LOC           VALID_FROM VALID_TO \n---------- ---------- -------------- ------------- ---------- ---------\n         1         10 ACCOUNTING     NEW YORK      01-JAN-80  28-FEB-90\n         5         10 Accounting     NEW YORK      01-MAR-90  31-MAR-90\n         9         10 Accounting     New York      01-APR-90  31-DEC-99\n\n3 rows selected.\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 8: querying using the new generic interface<\/em><\/strong><\/p>\n\n\n\n<p>And another nice thing: you can use binds, if you want. Because it&#8217;s not possible to declare bind variable of type DATE in SQL*Plus, I will demonstrate it with a PL\/SQL block. We are using a bind variable here and the explain plan is showing that binds were used in a filter expression. We just have full control here: use binds in SQM call and you&#8217;ll get binds in your query, use literals and you&#8217;ll get literals.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSQL&gt; -- Show I can use binds\nSQL&gt; declare\nv_cnt pls_integer;\nv_date date := DATE &#039;1990-02-01&#039;;\nbegin\n  select count(*)\n  into   v_cnt \n  from   time_machine.vw(deptv, v_date);\nend;\n PL\/SQL procedure successfully completed.\n\n...\nSQL&gt; select * from dbms_xplan.display_cursor(sql_id=&gt;&#039;8qr9z6n9gfm2k&#039;)\n\nPLAN_TABLE_OUTPUT                                                               \n-------------------------------------\nSQL_ID  8qr9z6n9gfm2k, child number 0                                           \n-------------------------------------                                           \nSELECT COUNT(*) FROM TIME_MACHINE.VW(DEPTV, :B1 )                               \n                                                                                \nPlan hash value: 1461709607                                                     \n                                                                                \n----------------------------------------------------------------------------    \n| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |    \n----------------------------------------------------------------------------    \n|   0 | SELECT STATEMENT   |       |       |       |     3 (100)|          |    \n|   1 |  SORT AGGREGATE    |       |     1 |    16 |            |          |    \n|*  2 |   TABLE ACCESS FULL| DEPTV |    12 |   192 |     3   (0)| 00:00:01 |    \n----------------------------------------------------------------------------    \n                                                                                \nPredicate Information (identified by operation id):                             \n---------------------------------------------------                             \n                                                                                \n   2 - filter((&quot;DEPTV&quot;.&quot;VALID_FROM&quot;&lt;=:B1 AND &quot;DEPTV&quot;.&quot;VALID_TO&quot;&gt;=:B1))          \n                                                                                \n\n19 rows selected.\n<\/pre><\/div>\n\n\n<p><strong><em>Listing 9: using bind variables with SQL macro functions<\/em><\/strong><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Summary<\/h4>\n\n\n\n<p>I am really impressed with what we have been able to achieve here with SQL macros! On the one hand, one can disagree that we don&#8217;t actually have a parameterized view here. Yes, we don&#8217;t have a view at all. But on the other hand, we can use the SQM-function exactly the same way. We have built one function that can handle ALL tables, instead of having a lot of views. And moreover, we have not hidden any information from query optimizer. It can see literals or do bind peeking and use histograms if available.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>UPDATE 2024<\/strong> <\/h2>\n\n\n\n<p>If you want to see how you can still use <strong>existing views<\/strong> but let SQL macros do the work to &#8220;inject&#8221; parameters check out the second part of this post: <a href=\"https:\/\/blog.sqlora.com\/en\/parameterized-views-with-sql-macros-part-2\/\" target=\"_blank\" rel=\"noreferrer noopener\">Parameterized Views with SQL Macros (Part 2)<\/a><\/p>\n\n\n\n<p><\/p>\n\n\n<script async src=\"\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js?client=ca-pub-2925154690547867\" crossorigin=\"anonymous\"><\/script><ins class=\"adsbygoogle\" style=\"display:block; text-align:center;\" data-ad-client=\"ca-pub-2925154690547867\" \ndata-ad-slot=\"2727403138\" \ndata-ad-layout=\"in-article\"\ndata-ad-format=\"fluid\"><\/ins>\n<script> \n(adsbygoogle = window.adsbygoogle || []).push({}); \n<\/script>\n\n\n<div class=\"crp-list-container\"><h3 class=\"crp-list-title\">Related Posts<\/h3><ul class=\"crp-list\"><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/oracle-20c-sql-macros\/\" target=\"_blank\">Oracle 20c: SQL Macros<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/sql-macros-part-2-parameters-and-parsing\/\" target=\"_blank\">SQL Macros Part 2 &#8211; Passing of Parameters and Parsing<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/building-hash-keys-using-sql-macros-in-oracle-20c\/\" target=\"_blank\">Building Hash Keys using SQL Macros in Oracle 20c<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/temporal-joins-with-sql-macros-in-oracle-20c\/\" target=\"_blank\">Temporal Joins with SQL Macros in Oracle 20c<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/sql-macros-in-oracle-19c\/\" target=\"_blank\">SQL macros in Oracle 19c<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/en\/parameterized-views-with-sql-macros-part-2\/\" target=\"_blank\">Parameterized Views with SQL Macros (Part 2)<\/a><\/div><\/li><\/ul><\/div>","protected":false},"excerpt":{"rendered":"<p>Views have always been an efficient tool for encapsulating complex logic, creating defined access structures and so on. But there is one thing views cannot do: accept parameters. And this is unfortunately a big disadvantage in terms of flexibility. As a result, there are a number of workarounds, none of them without their drawbacks.<\/p>\n","protected":false},"author":1,"featured_media":1245,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[99,2,3,100],"tags":[106,103],"class_list":["post-1243","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-20c","category-oracle","category-sql","category-sql-macros","tag-parameterized-view","tag-sql-macros"],"_links":{"self":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1243","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/comments?post=1243"}],"version-history":[{"count":43,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1243\/revisions"}],"predecessor-version":[{"id":2385,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/posts\/1243\/revisions\/2385"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/media\/1245"}],"wp:attachment":[{"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/media?parent=1243"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/categories?post=1243"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.sqlora.com\/en\/wp-json\/wp\/v2\/tags?post=1243"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}