<?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>Transaction Time Archives - Philipp Salvisberg&#039;s Blog</title>
	<atom:link href="https://www.salvis.com/blog/tag/transaction-time/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.salvis.com/blog/tag/transaction-time/</link>
	<description>Database-centric development</description>
	<lastBuildDate>Wed, 08 Nov 2023 11:11:24 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://www.salvis.com/blog/wp-content/uploads/2014/04/favicon.png</url>
	<title>Transaction Time Archives - Philipp Salvisberg&#039;s Blog</title>
	<link>https://www.salvis.com/blog/tag/transaction-time/</link>
	<width>32</width>
	<height>32</height>
</image> 
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com"/>
<atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com"/>
<atom:link rel="hub" href="https://websubhub.com/hub"/>
<atom:link rel="self" href="https://www.salvis.com/blog/tag/transaction-time/feed/"/>
	<item>
		<title>Names Matter</title>
		<link>https://www.salvis.com/blog/2020/06/21/names-matter/</link>
					<comments>https://www.salvis.com/blog/2020/06/21/names-matter/#comments</comments>
		
		<dc:creator><![CDATA[Philipp Salvisberg]]></dc:creator>
		<pubDate>Sun, 21 Jun 2020 15:00:30 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[accessible_by_clause]]></category>
		<category><![CDATA[Decision Time]]></category>
		<category><![CDATA[Flashback]]></category>
		<category><![CDATA[Flashback Data Archive]]></category>
		<category><![CDATA[Jenkins]]></category>
		<category><![CDATA[PL/SQL Cop]]></category>
		<category><![CDATA[SonarQube]]></category>
		<category><![CDATA[Transaction Time]]></category>
		<category><![CDATA[Valid Time]]></category>
		<guid isPermaLink="false">https://www.salvis.com/blog/?p=9890</guid>

					<description><![CDATA[<p>This is one of my favourite quotes: There are only two hard things in Computer Science: cache invalidation and naming things.&#8212; Phil Karlton IT is my daily life. And this quote is so true. Lately, I&#8217;ve been thinking much more than usual about naming, and that names really matter. This led to<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2020/06/21/names-matter/">Names Matter</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>This is one of my favourite quotes:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>There are only two hard things in Computer Science: cache invalidation and naming things.<br />&#8212; Phil Karlton</p>
</blockquote>



<p>IT is my daily life. And this quote is so true. Lately, I&#8217;ve been thinking much more than usual about naming, and that names really matter. This led to some refactoring activities.</p>



<h2 class="wp-block-heading">Why Is Naming Important?</h2>



<p>When a person with German mother tongue hears the word &#8220;eagle&#8221;, she or he automatically associates it with a &#8220;hedgehog&#8221;. Simply because the German word for it (&#8220;Igel&#8221;) is pronounced exactly the same. Of course, language skills and the concrete context play a role. The point is, a wrong association is likely. When we give a name to a thing, we basically want to avoid such false associations. In the best case, they are not helpful. In the worst case, this leads to rejection, as the next example shows.</p>



<p>In 1982 Mitsubishi Motors launched a SUV with the name &#8220;Pajero&#8221;. This name had to be changed in some regions, because &#8220;pajero&#8221; means &#8220;wanker&#8221; in Spanish. This example also shows that it is more important what others think about a name than we do.</p>



<p>In IT we have to name many things. Databases, schemas, tables, columns, views, packages, triggers, variables, fields, methods, classes, modules, components, products, etc. etc. Using an established name with a known and accepted definition help others to understand it better.</p>



<p>When we use a name, it is actually associated with a definition and properties, whether we like it or not. When names have a common and widely accepted meaning, it simplifies the communication. For example &#8220;banana&#8221;. Everybody knows what it means. <a href="https://www.merriam-webster.com/dictionary/banana">Merriam-Webster&#8217;s definition</a> is:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>An elongated usually tapering tropical fruit with soft pulpy flesh enclosed in a soft usually yellow rind.</p>
</blockquote>



<p>And I am sure that each of us could add a few characteristics to this definition.</p>



<h2 class="wp-block-heading">Why Is Naming Difficult?</h2>



<p>A name must fulfil many characteristics. For example</p>



<ul class="wp-block-list">
<li>Short</li>



<li>Fitting (naturally relates to the intended meaning, and characteristics)</li>



<li>Easy to spell,&nbsp;pronounce, remember</li>



<li>Not associated with unwanted characteristics</li>



<li>Common and widely accepted meaning and definition, that fits the intention (for names without commercial value)</li>



<li>New, not used already (for marketable names)</li>
</ul>



<p>Depending on context there are some goal conflicts. However, even without a major conflict, it is difficult to name something adequately in the early stages. Because we do not know enough about the thing we want to name. Hence, we use an iterative approach. We name something (e.g. an entity, package or class) and while working on it we find out that the name does not fit (anymore) and we change it. Maybe we split the thing and have to name now two things, etc. etc.</p>



<p>Finding a fitting name means doing some research. How have others named that thing? What is the definition of it? Does it fit 100 percent? This is an interesting and instructive work. In any case, it takes time. And at the time we need a new name, we want it now (e.g. when a wizard asks for a name). We can always rename it later, right? &#8211; Technically yes. And often we do. But the longer we wait, the less likely we are renaming.</p>



<h2 class="wp-block-heading">Are Some Names More Important Than Others?</h2>



<p>Yes. The more visible a name is the more important it is.</p>



<p>For example, the names behind an API are very easy to change. We do not have to ask anyone before changing it. It&#8217;s no problem as long as the API provides the same results. That&#8217;s one of the reasons we strive for tight APIs, right? To get some leeway.</p>



<p>As soon as others are involved, we are not fully in control of the change anymore. For example, when I change a name in one of my blog posts, this change is visible immediately to everyone visiting my blog. But I cannot control the caches of others, like search engines, blog mirrors and other services that copy web content to third-party storages. Remember, cache invalidation is the other hard thing in IT.</p>



<p>As a consequence, before we release an artefact that becomes visible to others, we should take some time to verify the used names. We cannot take back what we&#8217;ve said (at least not completely). However, we are in control of what we say in the future.</p>



<h2 class="wp-block-heading">Banned Names on This Blog</h2>



<p>Some terms (names) were discussed recently (again) due to a series of sad events. I used these terms as well. I never really thought about them as &#8220;bad&#8221;. However, I&#8217;ve changed my mind. I&#8217;m part of the problem. And I do not like it. One thing I can do is to stop using terms, that a large group of people associate with slavery and racism. No big deal, right?</p>



<p>This is another quote I like very much:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>One cannot not communicate<br />&#8212; Paul Watzlawick</p>
</blockquote>



<p>It is difficult to draw a line for certain terms. However, I believe that &#8220;you cannot not decide&#8221;. You decide either explicitly or implicitly. Of course, very seldom something is purely black or white. It&#8217;s much more often a shade of grey. Some decision take some time. And that&#8217;s okay. But it is impossible to postpone a decision forever. At a certain point, it becomes a decision.</p>



<p>So, I decided to decommission some terms on this blog and introduce new ones. Here&#8217;s the list:</p>



<table id="tablepress-16" class="tablepress tablepress-id-16">
<thead>
<tr class="row-1">
	<th class="column-1">Current Term</th><th class="column-2">Decommissioned Term</th><th class="column-3">Context</th>
</tr>
</thead>
<tbody class="row-striping row-hover">
<tr class="row-2">
	<td class="column-1">accessible</td><td class="column-2"><del>white listed</del></td><td class="column-3">PL/SQL accessible_by clause</td>
</tr>
<tr class="row-3">
	<td class="column-1">agent</td><td class="column-2"><del>slave</del></td><td class="column-3">Jenkins</td>
</tr>
<tr class="row-4">
	<td class="column-1">exclusion list</td><td class="column-2"><del>blacklist</del></td><td class="column-3">PL/SQL Cop, PL/SQL accessible_by clause</td>
</tr>
<tr class="row-5">
	<td class="column-1">inclusion list</td><td class="column-2"><del>whitelist</del></td><td class="column-3">PL/SQL Cop, PL/SQL accessible_by clause</td>
</tr>
<tr class="row-6">
	<td class="column-1">main</td><td class="column-2"><del>master</del></td><td class="column-3">Git branch</td>
</tr>
<tr class="row-7">
	<td class="column-1">transaction structure data + enterprise structure data</td><td class="column-2"><del>master data</del></td><td class="column-3">Data modeling</td>
</tr>
<tr class="row-8">
	<td class="column-1">worker</td><td class="column-2"><del>slave</del></td><td class="column-3">Oracle DB background process </td>
</tr>
</tbody>
</table>
<!-- #tablepress-16 from cache -->


<p>Finding alternative names was surprisingly easy because others had already done the work and defined alternative names. They existed for years&#8230;</p>



<h3 class="wp-block-heading">Master Data</h3>



<p>However, finding an alternative for <a href="https://en.wikipedia.org/wiki/Master_data">master data</a> was harder. I reached out to my friends on Twitter. And got some helpful feedback. Finally, Robert Marti suggested having a look at <a href="https://www.linkedin.com/in/malcolmchisholm/">Malcolm Chisholm</a>&#8216;s book <a href="https://books.google.ch/books?id=WxtW5SUUAYAC&amp;printsec=frontcover#v=onepage&amp;q&amp;f=false">Managing Reference Data in Enterprise Databases</a>. On page <a href="https://books.google.ch/books?id=WxtW5SUUAYAC&amp;lpg=PP1&amp;pg=PA258#v=onepage&amp;q&amp;f=false">258ff</a> the different data classes are defined and explained. The book is from 2000. In the meantime Malcolm Chisholm has published revised definitions <a href="https://www.topquadrant.com/docs/whitepapers/TopBraid_ReferenceDataManagementWhitepaper-3-18-15.pdf">here</a> and <a href="https://www.topquadrant.com/resources/blogs/semantic-ecosystem-journal/docs/RefDataPrepMCRC-Final060215.pdf">here</a>.</p>



<p>In the next subchapter, I repeat the definition of the data groups defined by Malcolm Chisholm on slide 5 in <a href="https://www.topquadrant.com/resources/blogs/semantic-ecosystem-journal/docs/RefDataPrepMCRC-Final060215.pdf">this deck</a>. I like these definitions and plan to use them in the future.</p>



<h4 class="wp-block-heading">Metadata</h4>



<p>The data that describes all aspects of an enterprise’s information assets, and enables the enterprise to effectively use and manage these assets.</p>



<p><em>Here it is confined to the structure of databases. Found in a database’s system catalog. Sometimes included in database tables.</em></p>



<h4 class="wp-block-heading">Reference Data</h4>



<p>Any kind of data that is used solely to categorize other data found in a database, or solely for relating data in a database to information beyond the boundaries of the enterprise.</p>



<p><em>Codes and descriptions. Tables containing this data usually have just a few rows and columns.</em></p>



<h4 class="wp-block-heading">Transaction Structure Data</h4>



<p>Data that represents the direct participants in a transaction, and which must be present before a transaction fires.</p>



<p><em>The parties to the transactions of the enterprise. E.g. Customer, Product.</em></p>



<h4 class="wp-block-heading">Enterprise Structure Data</h4>



<p>Data that permits business activity to be reported and/or analyzed by business responsibility.</p>



<p><em>Typically, data that describes the structure of the enterprise. E.g. organizational or financial structure.</em></p>



<h4 class="wp-block-heading">Transaction Activity Data</h4>



<p>Data that represents the operations an enterprise carries out.</p>



<p><em>Traditional focus of IT – in many enterprises the only focus.</em></p>



<h4 class="wp-block-heading">Transaction Audit Data</h4>



<p>Data that tracks the life cycle of individual transactions.</p>



<p><em>Includes application logs, database logs, web server logs<span class="s1">.</span></em></p>



<h2 class="wp-block-heading">Summary</h2>



<p>You use a name to simplify communication. A name is a proxy for a longer definition and meaning. If the meaning is badly received by others and especially by the target community, this does not simplify communication. Using a different name sounds like a simple solution. Why not, if changing a name is simple enough?</p>



<p>In this case, I only had to edit a few blog posts. I handled them like typos. This means that I did not add any update information. I also had to register new URL redirects. That was straightforward. However, changing the branch name in 26 GitHub repositories was a bit more work than anticipated, because I also had to change URLs in several related files. For certain GitHub pages, I had to keep a non-default master branch. I suppose that sooner or later GitHub will allow me to get rid of them as well. If I had to change more repositories, I would probably automate this task.</p>



<p>Most of the time I spent finding an alternative name for &#8220;master data&#8221;. In the end, I learned something new and found good names and definitions. That will help me in the future.</p>
<p>The post <a href="https://www.salvis.com/blog/2020/06/21/names-matter/">Names Matter</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.salvis.com/blog/2020/06/21/names-matter/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Bitemp Remodeler v0.1.0 Released</title>
		<link>https://www.salvis.com/blog/2016/09/25/bitemp-remodeler-v0-1-0-released/</link>
		
		<dc:creator><![CDATA[Philipp Salvisberg]]></dc:creator>
		<pubDate>Sun, 25 Sep 2016 21:21:21 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Flashback]]></category>
		<category><![CDATA[Flashback Data Archive]]></category>
		<category><![CDATA[oddgen]]></category>
		<category><![CDATA[PL/SQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQL Developer]]></category>
		<category><![CDATA[Temporal Database]]></category>
		<category><![CDATA[Transaction Time]]></category>
		<category><![CDATA[Valid Time]]></category>
		<guid isPermaLink="false">https://www.salvis.com/blog/?p=7254</guid>

					<description><![CDATA[<p>I&#8217;ve been working on a flexible table API generator for Oracle Databases for several months. A TAPI generator doesn&#8217;t sound like a real innovation. But this one contains some features you probably have not seen before in the TAPI generator and hopefully will like it as much as I do. In this post, I will<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2016/09/25/bitemp-remodeler-v0-1-0-released/">Bitemp Remodeler v0.1.0 Released</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve been working on a flexible table API generator for Oracle Databases for several months. A TAPI generator doesn&#8217;t sound like a real innovation. But this one contains some features you probably have not seen before in the TAPI generator and hopefully will like it as much as I do.</p>



<p>In this post, I will not explain the feature set thoroughly. Instead, I will more or less focus on one of my favourite features.</p>



<h2 class="wp-block-heading">Four models</h2>



<p>The generator knows the following four data models.</p>



<figure class="wp-block-image"><a href="https://www.salvis.com/blog/wp-content/uploads/2016/09/four_models.png"><img fetchpriority="high" decoding="async" width="1424" height="643" src="https://www.salvis.com/blog/wp-content/uploads/2016/09/four_models.png" alt="four_models" class="wp-image-7258" srcset="https://www.salvis.com/blog/wp-content/uploads/2016/09/four_models.png 1424w, https://www.salvis.com/blog/wp-content/uploads/2016/09/four_models-300x135.png 300w, https://www.salvis.com/blog/wp-content/uploads/2016/09/four_models-768x347.png 768w, https://www.salvis.com/blog/wp-content/uploads/2016/09/four_models-1024x462.png 1024w" sizes="(max-width:767px) 480px, (max-width:1424px) 100vw, 1424px" /></a></figure>



<p>If your table is based on one of these four models you may</p>



<ol class="wp-block-list">
<li>simply generate a table API for it&nbsp;or</li>



<li>switch to another model and optionally generate a table API as well.</li>
</ol>



<p>Option 2) is extraordinary, since it will preserve the existing data. E.g. it will preserve the content of the flashback data archive when you switch your model from uni-temporal transaction time to a bi-temporal model even if the flashback archive tables need to be moved to another table. Furthermore, it will keep the interface for the latest table the same. No application change is required. Everything with just a few mouse clicks. If this sounds interesting to you, then have a look at https://github.com/oddgen/bitemp/blob/main/README.md where the concept is briefly explained or join me in my session &#8220;oddgen &#8211; Bi-temporal Table API in Action&#8221; at the <a href="http://www.trivadis.com/en/training/more-just-performance-days-2016-tvdpdays">More than just – Performance Days 2016</a>. Remote participation is still possible.</p>



<p>Option 1) is what we had for years. It was part of Oracle Designer, it&#8217;s part of SQL Developer in a simplified way and there are some more or less simple table API generators around. So no big deal. However, when you choose option 1), there is one cool part. The hook API package concept.</p>



<h2 class="wp-block-heading">The Hook API</h2>



<p>The problem with a lot of table API solutions is, that there is typically no developer-friendly way to include the business logic. I&#8217;ve seen the following:</p>



<ul class="wp-block-list">
<li>Manual changes of the generated code, which is for various reasons not a good solution.</li>



<li>External hooks, e.g. in XML files, INI files, relational tables, etc. and merged at generation time into the final code. Oracle Designer worked that way.</li>



<li>Code which is dynamically executed by the generator at runtime, e.g. code snippets is stored in a pre-defined way in relational tables.</li>
</ul>



<p>But what I&#8217;ve never seen, was business logic implemented in manually crafted PL/SQL packages, separated from the PL/SQL generated code. That&#8217;s strange because this is a common practice in Java-based projects.</p>



<p>In Java, you typically define an interface for that and configure at runtime the right implementation. In PL/SQL we may do that similarly. A PL/SQL specification is an interface definition. That just one implementation may exist for an interface is not a limiting factor in this case.</p>



<p>Bitemp Remodeler generates the following hook API package specification for the famous EMP&nbsp;table in schema SCOTT:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Package specification EMP_HOOK</span><span role="button" tabindex="0" data-code="CREATE OR REPLACE PACKAGE emp_hook AS
   /** 
   * Hooks called by non-temporal API for table emp_lt (see package body of emp_api)
   * generated by Bitemp Remodeler for SQL Developer.
   * The body of this package is not generated. It has to be crafted and maintained manually. 
   * Since the API for table emp_lt ignores errors caused by a missing hook package body, the implementation is optional.
   *
   * @headcom
   */

   /**
   * Hook called before insert into non-temporal table emp_lt.
   *
   * @param io_new_row new Row to be inserted
   */
   PROCEDURE pre_ins (
      io_new_row IN OUT emp_ot
   );

   /**
   * Hook called after insert into non-temporal table emp_lt.
   *
   * @param in_new_row new Row to be inserted
   */
   PROCEDURE post_ins (
      in_new_row IN emp_ot
   );

   /**
   * Hook called before update non-temporal table emp_lt.
   *
   * @param io_new_row Row with updated column values
   * @param in_old_row Row with original column values
   */
   PROCEDURE pre_upd (
      io_new_row IN OUT emp_ot,
      in_old_row IN emp_ot
   );

   /**
   * Hook called after update non-temporal table emp_lt.
   *
   * @param in_new_row Row with updated column values
   * @param in_old_row Row with original column values
   */
   PROCEDURE post_upd (
      in_new_row IN emp_ot,
      in_old_row IN emp_ot
   );

   /**
   * Hook called before delete from non-temporal table emp_lt.
   *
   * @param in_old_row Row with original column values
   */
   PROCEDURE pre_del (
      in_old_row IN emp_ot
   );

   /**
   * Hook called after delete from non-temporal table emp_lt.
   *
   * @param in_old_row Row with original column values
   */
   PROCEDURE post_del (
      in_old_row IN emp_ot
   );

END emp_hook;
/" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">CREATE OR REPLACE</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">PACKAGE</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">emp_hook</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">AS</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">/** </span></span>
<span class="line"><span style="color: #6A9955">   * Hooks called by non-temporal API for table emp_lt (see package body of emp_api)</span></span>
<span class="line"><span style="color: #6A9955">   * generated by Bitemp Remodeler for SQL Developer.</span></span>
<span class="line cbp-line-highlight"><span style="color: #6A9955">   * The body of this package is not generated. It has to be crafted and maintained manually. </span></span>
<span class="line cbp-line-highlight"><span style="color: #6A9955">   * Since the API for table emp_lt ignores errors caused by a missing hook package body, the implementation is optional.</span></span>
<span class="line"><span style="color: #6A9955">   *</span></span>
<span class="line"><span style="color: #6A9955">   * @headcom</span></span>
<span class="line"><span style="color: #6A9955">   */</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">   * Hook called before insert into non-temporal table emp_lt.</span></span>
<span class="line"><span style="color: #6A9955">   *</span></span>
<span class="line"><span style="color: #6A9955">   * @param io_new_row new Row to be inserted</span></span>
<span class="line"><span style="color: #6A9955">   */</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">PROCEDURE</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">pre_ins</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">      io_new_row </span><span style="color: #569CD6">IN</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">OUT</span><span style="color: #D4D4D4"> emp_ot</span></span>
<span class="line"><span style="color: #D4D4D4">   );</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">   * Hook called after insert into non-temporal table emp_lt.</span></span>
<span class="line"><span style="color: #6A9955">   *</span></span>
<span class="line"><span style="color: #6A9955">   * @param in_new_row new Row to be inserted</span></span>
<span class="line"><span style="color: #6A9955">   */</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">PROCEDURE</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">post_ins</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">      in_new_row </span><span style="color: #569CD6">IN</span><span style="color: #D4D4D4"> emp_ot</span></span>
<span class="line"><span style="color: #D4D4D4">   );</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">   * Hook called before update non-temporal table emp_lt.</span></span>
<span class="line"><span style="color: #6A9955">   *</span></span>
<span class="line"><span style="color: #6A9955">   * @param io_new_row Row with updated column values</span></span>
<span class="line"><span style="color: #6A9955">   * @param in_old_row Row with original column values</span></span>
<span class="line"><span style="color: #6A9955">   */</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">PROCEDURE</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">pre_upd</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">      io_new_row </span><span style="color: #569CD6">IN</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">OUT</span><span style="color: #D4D4D4"> emp_ot,</span></span>
<span class="line"><span style="color: #D4D4D4">      in_old_row </span><span style="color: #569CD6">IN</span><span style="color: #D4D4D4"> emp_ot</span></span>
<span class="line"><span style="color: #D4D4D4">   );</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">   * Hook called after update non-temporal table emp_lt.</span></span>
<span class="line"><span style="color: #6A9955">   *</span></span>
<span class="line"><span style="color: #6A9955">   * @param in_new_row Row with updated column values</span></span>
<span class="line"><span style="color: #6A9955">   * @param in_old_row Row with original column values</span></span>
<span class="line"><span style="color: #6A9955">   */</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">PROCEDURE</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">post_upd</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">      in_new_row </span><span style="color: #569CD6">IN</span><span style="color: #D4D4D4"> emp_ot,</span></span>
<span class="line"><span style="color: #D4D4D4">      in_old_row </span><span style="color: #569CD6">IN</span><span style="color: #D4D4D4"> emp_ot</span></span>
<span class="line"><span style="color: #D4D4D4">   );</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">   * Hook called before delete from non-temporal table emp_lt.</span></span>
<span class="line"><span style="color: #6A9955">   *</span></span>
<span class="line"><span style="color: #6A9955">   * @param in_old_row Row with original column values</span></span>
<span class="line"><span style="color: #6A9955">   */</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">PROCEDURE</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">pre_del</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">      in_old_row </span><span style="color: #569CD6">IN</span><span style="color: #D4D4D4"> emp_ot</span></span>
<span class="line"><span style="color: #D4D4D4">   );</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">/**</span></span>
<span class="line"><span style="color: #6A9955">   * Hook called after delete from non-temporal table emp_lt.</span></span>
<span class="line"><span style="color: #6A9955">   *</span></span>
<span class="line"><span style="color: #6A9955">   * @param in_old_row Row with original column values</span></span>
<span class="line"><span style="color: #6A9955">   */</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">PROCEDURE</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">post_del</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">      in_old_row </span><span style="color: #569CD6">IN</span><span style="color: #D4D4D4"> emp_ot</span></span>
<span class="line"><span style="color: #D4D4D4">   );</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">END</span><span style="color: #D4D4D4"> emp_hook;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span></code></pre></div>



<p>The generated table API calls before an INSERT the pre_ins procedure and after the INSERT the post_ins procedures. For DELETE and UPDATE this works the same way. On the highlighted line 5 and 6 two interested things are pointed out. The body is not generated and the body does not need to be implemented since the API ignores errors caused by a missing PL/SQL hook package body.</p>



<p>Technically this is solved as follows in the API package body:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-start:16;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Excerpt 1 of package body EMP_API</span><span role="button" tabindex="0" data-code="   e_hook_body_missing EXCEPTION;
   PRAGMA exception_init(e_hook_body_missing, -6508);" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">   e_hook_body_missing </span><span style="color: #569CD6">EXCEPTION</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #569CD6">PRAGMA exception_init</span><span style="color: #D4D4D4">(e_hook_body_missing, -</span><span style="color: #B5CEA8">6508</span><span style="color: #D4D4D4">);</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-start:167;--cbp-line-number-width:calc(3 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Excerpt 2 of package body EMP_API</span><span role="button" tabindex="0" data-code="      &lt;&lt;pre_ins&gt;&gt;
      BEGIN
         emp_hook.pre_ins(io_new_row =&gt; l_new_row);
      EXCEPTION
         WHEN e_hook_body_missing THEN
            NULL;
      END pre_ins;
      do_ins(io_row =&gt; l_new_row);" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">      &lt;&lt;pre_ins&gt;&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">BEGIN</span></span>
<span class="line"><span style="color: #D4D4D4">         emp_hook.pre_ins(io_new_row =&gt; </span><span style="color: #9CDCFE">l_new_row</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">EXCEPTION</span></span>
<span class="line"><span style="color: #D4D4D4">         </span><span style="color: #569CD6">WHEN</span><span style="color: #D4D4D4"> e_hook_body_missing </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">NULL</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #569CD6">END</span><span style="color: #D4D4D4"> pre_ins;</span></span>
<span class="line"><span style="color: #D4D4D4">      do_ins(io_row =&gt; </span><span style="color: #9CDCFE">l_new_row</span><span style="color: #D4D4D4">);</span></span></code></pre></div>



<p>Now you may ask what the performance impact of these e_hook_body_missing exceptions are. I&#8217;ve done a small test and called a procedure without and with implemented body 1 million times. The overhead of the missing body exception is about 7 microseconds per call. Here&#8217;s the test output from SQL Developer, the relevant lines 51 and 89 are highlighted.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Overhead of missing package body</span><span role="button" tabindex="0" data-code="SQL&gt; SET FEEDBACK ON
SQL&gt; SET ECHO ON
SQL&gt; SET TIMING ON
SQL&gt; DROP PACKAGE dummy_api;

Package DUMMY_API dropped.

Elapsed: 00:00:00.027
SQL&gt; DROP PACKAGE dummy_hook;

Package DUMMY_HOOK dropped.

Elapsed: 00:00:00.030
SQL&gt; CREATE OR REPLACE PACKAGE dummy_hook AS
   PROCEDURE pre_ins;
END dummy_hook;
/

Package DUMMY_HOOK compiled

Elapsed: 00:00:00.023
SQL&gt; CREATE OR REPLACE PACKAGE dummy_api AS
   PROCEDURE ins;
END dummy_api;
/

Package DUMMY_API compiled

Elapsed: 00:00:00.034
SQL&gt; CREATE OR REPLACE PACKAGE BODY dummy_api AS
   e_hook_body_missing EXCEPTION;
   PRAGMA exception_init(e_hook_body_missing, -6508);  
   PROCEDURE ins IS
   BEGIN
      BEGIN
         dummy_hook.pre_ins;
      EXCEPTION
         WHEN e_hook_body_missing THEN
            NULL;
      END pre_ins;
      dbms_output.put('.');
   END ins;
END dummy_api;
/

Package body DUMMY_API compiled

Elapsed: 00:00:00.040
SQL&gt; -- without hook body
SQL&gt; BEGIN
   FOR i IN 1..1E6 LOOP
      dummy_api.ins;
   END LOOP;
END;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:07.878
SQL&gt; CREATE OR REPLACE PACKAGE BODY dummy_hook AS
   PROCEDURE pre_ins IS
   BEGIN
      dbms_output.put('-');
   END pre_ins;
END dummy_hook;
/

Package body DUMMY_HOOK compiled

Elapsed: 00:00:00.029
SQL&gt; -- with hook body
SQL&gt; BEGIN
   FOR i IN 1..1E6 LOOP
      dummy_api.ins;
   END LOOP;
END;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.632" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQL&gt; SET FEEDBACK ON</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; SET ECHO ON</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; SET TIMING ON</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; DROP PACKAGE dummy_api;</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Package DUMMY_API dropped.</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Elapsed: 00:00:00.027</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; DROP PACKAGE dummy_hook;</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Package DUMMY_HOOK dropped.</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Elapsed: 00:00:00.030</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; CREATE OR REPLACE PACKAGE dummy_hook AS</span></span>
<span class="line"><span style="color: #D4D4D4">   PROCEDURE pre_ins;</span></span>
<span class="line"><span style="color: #D4D4D4">END dummy_hook;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Package DUMMY_HOOK compiled</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Elapsed: 00:00:00.023</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; CREATE OR REPLACE PACKAGE dummy_api AS</span></span>
<span class="line"><span style="color: #D4D4D4">   PROCEDURE ins;</span></span>
<span class="line"><span style="color: #D4D4D4">END dummy_api;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Package DUMMY_API compiled</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Elapsed: 00:00:00.034</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; CREATE OR REPLACE PACKAGE BODY dummy_api AS</span></span>
<span class="line"><span style="color: #D4D4D4">   e_hook_body_missing EXCEPTION;</span></span>
<span class="line"><span style="color: #D4D4D4">   PRAGMA exception_init(e_hook_body_missing, -6508);  </span></span>
<span class="line"><span style="color: #D4D4D4">   PROCEDURE ins IS</span></span>
<span class="line"><span style="color: #D4D4D4">   BEGIN</span></span>
<span class="line"><span style="color: #D4D4D4">      BEGIN</span></span>
<span class="line"><span style="color: #D4D4D4">         dummy_hook.pre_ins;</span></span>
<span class="line"><span style="color: #D4D4D4">      EXCEPTION</span></span>
<span class="line"><span style="color: #D4D4D4">         WHEN e_hook_body_missing THEN</span></span>
<span class="line"><span style="color: #D4D4D4">            NULL;</span></span>
<span class="line"><span style="color: #D4D4D4">      END pre_ins;</span></span>
<span class="line"><span style="color: #D4D4D4">      dbms_output.put(&#39;.&#39;);</span></span>
<span class="line"><span style="color: #D4D4D4">   END ins;</span></span>
<span class="line"><span style="color: #D4D4D4">END dummy_api;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Package body DUMMY_API compiled</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Elapsed: 00:00:00.040</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; -- without hook body</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; BEGIN</span></span>
<span class="line"><span style="color: #D4D4D4">   FOR i IN 1..1E6 LOOP</span></span>
<span class="line"><span style="color: #D4D4D4">      dummy_api.ins;</span></span>
<span class="line"><span style="color: #D4D4D4">   END LOOP;</span></span>
<span class="line"><span style="color: #D4D4D4">END;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">PL/SQL procedure successfully completed.</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">Elapsed: 00:00:07.878</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; CREATE OR REPLACE PACKAGE BODY dummy_hook AS</span></span>
<span class="line"><span style="color: #D4D4D4">   PROCEDURE pre_ins IS</span></span>
<span class="line"><span style="color: #D4D4D4">   BEGIN</span></span>
<span class="line"><span style="color: #D4D4D4">      dbms_output.put(&#39;-&#39;);</span></span>
<span class="line"><span style="color: #D4D4D4">   END pre_ins;</span></span>
<span class="line"><span style="color: #D4D4D4">END dummy_hook;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Package body DUMMY_HOOK compiled</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Elapsed: 00:00:00.029</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; -- with hook body</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; BEGIN</span></span>
<span class="line"><span style="color: #D4D4D4">   FOR i IN 1..1E6 LOOP</span></span>
<span class="line"><span style="color: #D4D4D4">      dummy_api.ins;</span></span>
<span class="line"><span style="color: #D4D4D4">   END LOOP;</span></span>
<span class="line"><span style="color: #D4D4D4">END;</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">PL/SQL procedure successfully completed.</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">Elapsed: 00:00:00.632</span></span></code></pre></div>



<p>It makes sense to provide a body with a NULL implementation to avoid the small overhead of handling the missing body exception.</p>



<p>Nonetheless, the way the business logic is separated from the generated code is one of the many things I like about Bitemp Remodeler.</p>



<p>Download Bitemp Remodeler from the&nbsp;<a href="https://www.salvis.com/blog/download/">Download</a> section on&nbsp;my blog or install it directly via the SQL Developer update site <a href="http://update.oddgen.org/">http://update.oddgen.org/</a></p>
<p>The post <a href="https://www.salvis.com/blog/2016/09/25/bitemp-remodeler-v0-1-0-released/">Bitemp Remodeler v0.1.0 Released</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Multi-temporal Features in Oracle 12c</title>
		<link>https://www.salvis.com/blog/2014/01/04/multi-temporal-database-features-in-oracle-12c/</link>
					<comments>https://www.salvis.com/blog/2014/01/04/multi-temporal-database-features-in-oracle-12c/#comments</comments>
		
		<dc:creator><![CDATA[Philipp Salvisberg]]></dc:creator>
		<pubDate>Sat, 04 Jan 2014 12:19:18 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Decision Time]]></category>
		<category><![CDATA[Flashback]]></category>
		<category><![CDATA[Flashback Data Archive]]></category>
		<category><![CDATA[Temporal Database]]></category>
		<category><![CDATA[Transaction Time]]></category>
		<category><![CDATA[Valid Time]]></category>
		<guid isPermaLink="false">http://www.salvis.com/blog/?p=1009</guid>

					<description><![CDATA[<p>Oracle 12c has a feature called Temporal Validity. With Temporal Validity, you can add one or more valid time dimensions to a table using existing columns, or using columns automatically created by the database. This means that Oracle offers combined with Flashback Data Archive native bi-temporal and even multi-temporal historization features. This<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2014/01/04/multi-temporal-database-features-in-oracle-12c/">Multi-temporal Features in Oracle 12c</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Oracle 12c has a feature called <em>Temporal Validity</em>. With Temporal Validity, you can add one or more valid time dimensions to a table using existing columns, or using columns automatically created by the database. This means that Oracle offers combined with Flashback Data Archive native bi-temporal and even multi-temporal historization features. This blog post explains the different types of historization, when and how to use them and positions the most recent Oracle 12c database features.</p>



<h2 class="wp-block-heading">Semantics and Granularity of Periods</h2>



<p>In Flashback Data Archive Oracle defines periods with a half-open interval. This means that a point in time <strong>x</strong> is part of a period if <strong>x</strong> &gt;= the start of the period and<strong> x</strong> &lt; the end of the period. It is no surprise that Oracle uses also half-open intervals for Temporal Validity. The following figure visualizes the principle:</p>



<figure class="wp-block-image is-resized"><a href="//www.salvis.com/blog/wp-content/uploads/2014/01/Period_Semantics_and_Granularity.png"><img decoding="async" width="1203" height="182" src="//www.salvis.com/blog/wp-content/uploads/2014/01/Period_Semantics_and_Granularity.png" alt="Period, Semantics and Granularity" class="wp-image-1013" style="width:601px" title="Period, Semantics and Granularity" srcset="https://www.salvis.com/blog/wp-content/uploads/2014/01/Period_Semantics_and_Granularity.png 1203w, https://www.salvis.com/blog/wp-content/uploads/2014/01/Period_Semantics_and_Granularity-300x45.png 300w, https://www.salvis.com/blog/wp-content/uploads/2014/01/Period_Semantics_and_Granularity-1024x154.png 1024w" sizes="(max-width:767px) 480px, (max-width:1203px) 100vw, 1203px" /></a><figcaption class="wp-element-caption"> Fig. 1: Semantics and Granularity of Periods</figcaption></figure>



<p>The advantage of a half-open interval is that the end of a preceding period is identical to the start of the subsequent period. Thus there is no gap and the granularity of a period (year, month, day, second, millisecond, nanosecond, etc.) is irrelevant. The disadvantage is that querying data at a point in time using a traditional WHERE clause is a bit more verbose compared to closed intervals since BETWEEN conditions are not applicable.</p>



<p>Furthermore, Oracle uses NULL for&nbsp;-∞&nbsp;und +∞. Considering this information the WHERE clause to filter the currently valid periods looks as follows:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">1) Current Periods</span><span role="button" tabindex="0" data-code="WHERE (vt_start IS NULL OR vt_start <= SYSTIMESTAMP)
  AND (vt_end IS NULL OR vt_end &gt; SYSTIMESTAMP)" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">WHERE</span><span style="color: #D4D4D4"> (vt_start </span><span style="color: #569CD6">IS</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">NULL</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">OR</span><span style="color: #D4D4D4"> vt_start &lt;= SYSTIMESTAMP)</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">AND</span><span style="color: #D4D4D4"> (vt_end </span><span style="color: #569CD6">IS</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">NULL</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">OR</span><span style="color: #D4D4D4"> vt_end &gt; SYSTIMESTAMP)</span></span></code></pre></div>



<h2 class="wp-block-heading">Use of Temporal Periods</h2>



<p>In an entity-relationship model temporal periods may be used for transaction structure data, enterprise structure data or reference data. For transaction activity data we do not need temporal periods since the data itself contains one or more timestamps. Corrections may be done through a reversal or difference posting logic, similar to bookkeeping transactions.</p>



<p>The situation is similar in a dimensional model. Dimensions correspond to transaction structure data, enterprise structure and reference data and may have a temporal period (e.g. slowly changing dimensions type 2). Facts do not have temporal periods. Instead, they are modeled with one or more relationships to the time dimension. A fact is immutable. Changes are applied through new facts using a reversal or difference posting logic.</p>



<h2 class="wp-block-heading">Transaction Time &#8211; TT</h2>



<p>A flight data recorder collects and records various metrics during a flight to allow the reconstruction of the past. The transaction or system time in a data model is comparable to the functionality of such a flight data recorder. A table with a transaction time axis allows to query the current and the past state, but changes in the past or the future are not possible.</p>



<p>Example: Scott becomes a manager. The change of the job description from &#8220;Analyst&#8221; to &#8220;Manager&#8221; was entered into the system on April 15, 2013, at 15:42:42. The previous description Analyst is terminated at this point in time and the new description Manager becomes current at exactly the same point in time.</p>



<p>Oracle supports the transaction time with Flashback Data Archive (formally known as Total Recall). Using Flashback Data Archive you may query a consistent state of the past.</p>



<p><span style="line-height: 1.5em;">
<table id="tablepress-4" class="tablepress tablepress-id-4" aria-labelledby="tablepress-4-name">
<thead>
<tr class="row-1">
	<th class="column-1">SCN</th><th class="column-2">Session A</th><th class="column-3">Session B</th>
</tr>
</thead>
<tbody class="row-striping">
<tr class="row-2">
	<td class="column-1">1</td><td class="column-2">INSERT INTO emp<br />
&nbsp;&nbsp;&nbsp;(empno, ename, job, sal, deptno)<br />
VALUES<br />
&nbsp;&nbsp;&nbsp;(4242, 'CARTER', 'CLERK', '2400', 20);</td><td class="column-3"></td>
</tr>
<tr class="row-3">
	<td class="column-1">2</td><td class="column-2">SELECT COUNT(*)<br />
&nbsp;&nbsp;FROM emp; -- <font color="red"/>15</font> rows</td><td class="column-3"></td>
</tr>
<tr class="row-4">
	<td class="column-1">3</td><td class="column-2"></td><td class="column-3">SELECT COUNT(*) <br />
&nbsp;&nbsp;FROM emp; -- <font color="red"/>14</font> rows</td>
</tr>
<tr class="row-5">
	<td class="column-1">4</td><td class="column-2">COMMIT;</td><td class="column-3"></td>
</tr>
</tbody>
</table>
<h2 id="tablepress-4-name" class="tablepress-table-name tablepress-table-name-id-4">Tab. 1: Consistent View of the Past</h2>
</span></p>



<p>What is the result of the query&nbsp;&#8220;SELECT COUNT(*) FROM emp AS OF SCN 3&#8221; based on table 1 above? &#8211; 14 rows. This is a good and reasonable representation of the past. However, it also shows, that the consistent representation of the past is a matter of definition and in this case, it does not represent the situation of session A.</p>



<h2 class="wp-block-heading">Valid Time &#8211; VT</h2>



<p>The valid time describes the period during which something in the real world is considered valid. This period is independent of the entry into the system and therefore needs to be maintained explicitly. Changes and queries are supported in the past as well as in the future.</p>



<p>Example: Scott becomes a manager. The change of the job description from &#8220;Analyst&#8221; to &#8220;Manager&#8221; is valid from January 1 2014.&nbsp;The previous description Analyst is terminated at this point in time and the new description Manager becomes valid at exactly the same point in time. It is irrelevant when this change is entered into the System.</p>



<h2 class="wp-block-heading">Decision Time &#8211; DT</h2>



<p>The decision time describes the date and time a decision has been made. This point in time is independent of an entry into the System and is not directly related to the valid time period. Future changes are not possible.</p>



<p>Example: Scott becomes manager. The decision to change the job description from &#8220;Analyst&#8221; to &#8220;Manager&#8221; was made on March 24 2013. The previous job description Analyst is terminated on the decision time axis at this point in time and the new description Manager becomes current at exactly the same point in time on the decision time axis.&nbsp;It is irrelevant when this change is entered into the System and it is irrelevant when Scott may call himself officially a manager.</p>



<h2 class="wp-block-heading">Historization Types</h2>



<p>On one hand, the historization types are based on the time dimensions visualized in figure 2 and on the other hand categorized on the combination of these time dimensions. In this post only the most popular and generic time periods are covered. However, depending on the requirements additional, specific time periods are conceivable.</p>


<div class="wp-block-image center-figcaption">
<figure class="alignright size-full is-resized"><a href="https://www.salvis.com/blog/wp-content/uploads/2014/01/Historization_Types1.png"><img decoding="async" width="626" height="626" src="https://www.salvis.com/blog/wp-content/uploads/2014/01/Historization_Types1.png" alt="" class="wp-image-1039" style="width:313px" srcset="https://www.salvis.com/blog/wp-content/uploads/2014/01/Historization_Types1.png 626w, https://www.salvis.com/blog/wp-content/uploads/2014/01/Historization_Types1-150x150.png 150w, https://www.salvis.com/blog/wp-content/uploads/2014/01/Historization_Types1-300x300.png 300w, https://www.salvis.com/blog/wp-content/uploads/2014/01/Historization_Types1-330x330.png 330w" sizes="(max-width:767px) 480px, 626px" /></a><figcaption class="wp-element-caption">Fig. 2: Historization Types</figcaption></figure>
</div>


<p></p>



<p>Non-temporal models do not have any time dimensions (e.g. EMP and DEPT in Schema SCOTT).</p>



<p>Uni-temporal models use just one time dimension (e.g. transaction time or valid time).</p>



<p>Bi-temporal models use exactly two time dimensions (e.g. transaction time and valid time).</p>



<p>Multi-temporal models use at least three time dimensions.</p>



<p>Tri-temporal &nbsp;models are based exactly on three time dimensions.</p>



<h2 class="wp-block-heading">Temporal Validity</h2>



<p>The feature Temporal Validity covers the DDL and DML enhancements in Oracle 12c concerning temporal data management. The statements CREATE TABLE, ALTER TABLE and DROP TABLE have been extended by a new PERIOD FOR clause. Here is an example:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">2) Enable Temporal Validity</span><span role="button" tabindex="0" data-code="SQL&gt; ALTER TABLE dept ADD (
  2     vt_start DATE,
  3     vt_end   DATE,
  4     PERIOD FOR vt (vt_start, vt_end)
  5  );

SQL&gt; SELECT * FROM dept;	

    DEPTNO DNAME          LOC           VT_START   VT_END
---------- -------------- ------------- ---------- ----------
        10 ACCOUNTING     NEW YORK
        20 RESEARCH       DALLAS
        30 SALES          CHICAGO
        40 OPERATIONS     BOSTON" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQL&gt; ALTER TABLE dept ADD (</span></span>
<span class="line"><span style="color: #D4D4D4">  2     vt_start DATE,</span></span>
<span class="line"><span style="color: #D4D4D4">  3     vt_end   DATE,</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  4     PERIOD FOR vt (vt_start, vt_end)</span></span>
<span class="line"><span style="color: #D4D4D4">  5  );</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; SELECT * FROM dept;	</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">    DEPTNO DNAME          LOC           VT_START   VT_END</span></span>
<span class="line"><span style="color: #D4D4D4">---------- -------------- ------------- ---------- ----------</span></span>
<span class="line"><span style="color: #D4D4D4">        10 ACCOUNTING     NEW YORK</span></span>
<span class="line"><span style="color: #D4D4D4">        20 RESEARCH       DALLAS</span></span>
<span class="line"><span style="color: #D4D4D4">        30 SALES          CHICAGO</span></span>
<span class="line"><span style="color: #D4D4D4">        40 OPERATIONS     BOSTON</span></span></code></pre></div>



<p>VT names the period and is a hidden column. The association of the VT period to the VT_START and VT_END columns is stored in the Oracle Data Dictionary in the table SYS_FBA_PERIOD. You need a dedicated ALTER TABLE call for every additional period.</p>



<p>For every period a &nbsp;constraint is created to enforce positive time periods (VT_START &lt; VT_END). However, it is not possible to define temporal constraints, e.g. prohibit overlapping periods, gaps, or orphaned parent/child periods.</p>



<p>Oracle 12c does not deliver support for temporal DML. Desirable would be for example:</p>



<ul class="wp-block-list">
<li>insert, update delete for a given period</li>



<li>update a subset of columns for a given period</li>



<li>merge of connected and identical periods</li>
</ul>



<p>Hence temporal changes have to be implemented as a series of conventional DML. Here is an example:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">3) Temporal DML</span><span role="button" tabindex="0" data-code="SQL&gt; UPDATE dept SET vt_end = DATE '2014-01-01' WHERE deptno = 30;

SQL&gt; INSERT INTO dept (deptno, dname, loc, vt_start)
  2       VALUES (30, 'SALES', 'SAN FRANCISCO', DATE '2014-01-01');

SQL&gt; SELECT * FROM dept WHERE deptno = 30 ORDER BY vt_start NULLS FIRST;

 DEPTNO    DNAME          LOC           VT_START   VT_END
---------- -------------- ------------- ---------- ----------
        30 SALES          CHICAGO                  2014-01-01
        30 SALES          SAN FRANCISCO 2014-01-01" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQL&gt; UPDATE dept SET vt_end = DATE &#39;2014-01-01&#39; WHERE deptno = 30;</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; INSERT INTO dept (deptno, dname, loc, vt_start)</span></span>
<span class="line"><span style="color: #D4D4D4">  2       VALUES (30, &#39;SALES&#39;, &#39;SAN FRANCISCO&#39;, DATE &#39;2014-01-01&#39;);</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; SELECT * FROM dept WHERE deptno = 30 ORDER BY vt_start NULLS FIRST;</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4"> DEPTNO    DNAME          LOC           VT_START   VT_END</span></span>
<span class="line"><span style="color: #D4D4D4">---------- -------------- ------------- ---------- ----------</span></span>
<span class="line"><span style="color: #D4D4D4">        30 SALES          CHICAGO                  2014-01-01</span></span>
<span class="line"><span style="color: #D4D4D4">        30 SALES          SAN FRANCISCO 2014-01-01</span></span></code></pre></div>



<h2 class="wp-block-heading">Temporal Flashback Query</h2>



<p>The feature Temporal Flashback Query covers query enhancements in Oracle 12c concerning temporal data. Oracle extended the existing Flashback Query interfaces. The FLASHBACK_QUERY_CLAUSE of the SELECT statement has been extended by a PERIOD FOR clause. Here is an example:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">4) Temporal Flashback Query using SQL</span><span role="button" tabindex="0" data-code="SQL&gt; SELECT *
  2    FROM dept AS OF PERIOD FOR vt DATE '2015-01-01'
  3   ORDER BY deptno;

    DEPTNO DNAME          LOC           VT_START   VT_END
---------- -------------- ------------- ---------- ----------
        10 ACCOUNTING     NEW YORK
        20 RESEARCH       DALLAS
        30 SALES          SAN FRANCISCO 2014-01-01
        40 OPERATIONS     BOSTON" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQL&gt; SELECT *</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  2    FROM dept AS OF PERIOD FOR vt DATE &#39;2015-01-01&#39;</span></span>
<span class="line"><span style="color: #D4D4D4">  3   ORDER BY deptno;</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">    DEPTNO DNAME          LOC           VT_START   VT_END</span></span>
<span class="line"><span style="color: #D4D4D4">---------- -------------- ------------- ---------- ----------</span></span>
<span class="line"><span style="color: #D4D4D4">        10 ACCOUNTING     NEW YORK</span></span>
<span class="line"><span style="color: #D4D4D4">        20 RESEARCH       DALLAS</span></span>
<span class="line"><span style="color: #D4D4D4">        30 SALES          SAN FRANCISCO 2014-01-01</span></span>
<span class="line"><span style="color: #D4D4D4">        40 OPERATIONS     BOSTON</span></span></code></pre></div>



<p>Instead of &#8220;AS OF PERIOD FOR&#8221; you may also use &#8220;VERSIONS PERIOD FOR&#8221;. However, it is important to notice that you may not define multiple PERIOD FOR clauses. Hence you need to filter additional temporal periods in the WHERE clause.</p>



<p>The PERIOD FOR clause is not applicable for views. For views, the enhancement in the PL/SQL package DBMS_FLASHBACK_ARCHIVE is interesting, especially the procedures ENABLE_AT_VALID_TIME and DISABLE_ASOF_VALID_TIME to manage a temporal context. Here is an example:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">5) Temporal Flashback Query using PL/SQL Context</span><span role="button" tabindex="0" data-code="SQL&gt; BEGIN
  2     dbms_flashback_archive.enable_at_valid_time(
  3        level      =&gt; 'ASOF', 
  4        query_time =&gt; DATE '2015-01-01'
  5     );
  6  END;
  7  /

SQL&gt; SELECT * FROM dept ORDER BY deptno;

    DEPTNO DNAME          LOC           VT_START    VT_END
---------- -------------- ------------- ---------- ----------
        10 ACCOUNTING     NEW YORK
        20 RESEARCH       DALLAS
        30 SALES          SAN FRANCISCO 2014-01-01
        40 OPERATIONS     BOSTON" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQL&gt; BEGIN</span></span>
<span class="line"><span style="color: #D4D4D4">  2     dbms_flashback_archive.enable_at_valid_time(</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  3        level      =&gt; &#39;ASOF&#39;, </span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  4        query_time =&gt; DATE &#39;2015-01-01&#39;</span></span>
<span class="line"><span style="color: #D4D4D4">  5     );</span></span>
<span class="line"><span style="color: #D4D4D4">  6  END;</span></span>
<span class="line"><span style="color: #D4D4D4">  7  /</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; SELECT * FROM dept ORDER BY deptno;</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">    DEPTNO DNAME          LOC           VT_START    VT_END</span></span>
<span class="line"><span style="color: #D4D4D4">---------- -------------- ------------- ---------- ----------</span></span>
<span class="line"><span style="color: #D4D4D4">        10 ACCOUNTING     NEW YORK</span></span>
<span class="line"><span style="color: #D4D4D4">        20 RESEARCH       DALLAS</span></span>
<span class="line"><span style="color: #D4D4D4">        30 SALES          SAN FRANCISCO 2014-01-01</span></span>
<span class="line"><span style="color: #D4D4D4">        40 OPERATIONS     BOSTON</span></span></code></pre></div>



<p>Currently, it is not possible to define a temporal period and therefore the context is applied to every temporal period. In these cases, you have to set the context via the WHERE clause.</p>



<p>A limitation of Oracle 12.1.0.1 is that&nbsp;Temporal Flashback Query predicates are not applied in multitenant configuration. The PERIOD FOR clause in the SELECT statement and the DBMS_FLASHBACK_ARCHIVE.ENABLE_AT_VALID_TIME calls are simply ignored. This limitation has been lifted with Oracle 12.1.0.2.</p>



<p>Another limitation is, that Oracle 12 does not provide support for temporal joins and temporal aggregations.</p>



<h2 class="wp-block-heading">Tri-temporal Data Model</h2>



<p>The following data model is based on the EMP/DEPT model in the schema SCOTT. The table EMPV implements three temporal dimensions:</p>



<ul class="wp-block-list">
<li>Transaction time (TT) with Flashback Data Archive</li>



<li>Valid time (VT) with Temporal Validity</li>



<li>Decision time (DT) with Temporal Validity</li>
</ul>



<figure class="wp-block-image is-resized"><a href="//www.salvis.com/blog/wp-content/uploads/2014/01/Tri_Temporal_Datamodel1.png"><img loading="lazy" decoding="async" width="1294" height="943" src="//www.salvis.com/blog/wp-content/uploads/2014/01/Tri_Temporal_Datamodel1.png" alt="Tri-temporal Data Model" class="wp-image-1047" style="width:642px" title="Tri-temporal Data Model" srcset="https://www.salvis.com/blog/wp-content/uploads/2014/01/Tri_Temporal_Datamodel1.png 1294w, https://www.salvis.com/blog/wp-content/uploads/2014/01/Tri_Temporal_Datamodel1-300x218.png 300w, https://www.salvis.com/blog/wp-content/uploads/2014/01/Tri_Temporal_Datamodel1-1024x746.png 1024w" sizes="auto, (max-width:767px) 480px, (max-width:1294px) 100vw, 1294px" /></a><figcaption class="wp-element-caption">Fig. 3: Tri-temporal Data Model[</figcaption></figure>



<p><span style="line-height: 1.5em;"><span style="line-height: 1.5em;">The table EMP is reduced to the primary key (EMPNO) which is not temporal. This allows to define and enable the foreign key constraint EMPV_EMP_MGR_FK.</span></span></p>



<p>The following six events will be represented with this model.</p>



<p><span style="line-height: 1.5em;"><span style="line-height: 1.5em;">
<table id="tablepress-5" class="tablepress tablepress-id-5" aria-labelledby="tablepress-5-name">
<thead>
<tr class="row-1">
	<th class="column-1">No</th><th class="column-2">Transaction Time (TT)</th><th class="column-3">Valid Time (VT)</th><th class="column-4">Decision Time (DT)</th><th class="column-5">Action</th>
</tr>
</thead>
<tbody class="row-striping">
<tr class="row-2">
	<td class="column-1">#1</td><td class="column-2">1</td><td class="column-3"></td><td class="column-4"></td><td class="column-5">Initial load from SCOTT.EMP table<br />
</td>
</tr>
<tr class="row-3">
	<td class="column-1">#2</td><td class="column-2">2<br />
</td><td class="column-3">1990-01-01</td><td class="column-4"></td><td class="column-5">Change name from SCOTT to Scott</td>
</tr>
<tr class="row-4">
	<td class="column-1">#3</td><td class="column-2">3</td><td class="column-3">1991-04-01</td><td class="column-4"></td><td class="column-5">Scott leaves the company</td>
</tr>
<tr class="row-5">
	<td class="column-1">#4</td><td class="column-2">4</td><td class="column-3">1991-10-01</td><td class="column-4"></td><td class="column-5">Scott rejoins</td>
</tr>
<tr class="row-6">
	<td class="column-1">#5</td><td class="column-2">5</td><td class="column-3">1989-01-01</td><td class="column-4"></td><td class="column-5">Change job from ANALYST TO Analyst</td>
</tr>
<tr class="row-7">
	<td class="column-1">#6</td><td class="column-2">6</td><td class="column-3">2014-01-01</td><td class="column-4">2013-03-24</td><td class="column-5">Change job to Manager and double salary</td>
</tr>
</tbody>
</table>
<h2 id="tablepress-5-name" class="tablepress-table-name tablepress-table-name-id-5">Tab. 2: Events</h2>
</span></span></p>



<p>After the processing of all 6 events the periods for employee 7788 (Scott) in the table EMPV may be queried as follows. The transaction time is represented as the System Change Number SCN.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">6) Content after Event #6</span><span role="button" tabindex="0" data-code="SQL&gt; SELECT dense_rank() OVER(ORDER BY versions_startscn) event_no, empno, ename, job,
  2         sal, versions_startscn tt_start, versions_endscn tt_end,
  3         to_char(vt_start,'YYYY-MM-DD') vt_start, to_char(vt_end,'YYYY-MM-DD') vt_end,
  4         to_CHAR(dt_start,'YYYY-MM-DD') dt_start, to_char(dt_end,'YYYY-MM-DD') dt_end
  5    FROM empv VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE
  6   WHERE empno = 7788 AND versions_operation IN ('I','U')
  7   ORDER BY tt_start, vt_start NULLS FIRST, dt_start NULLS FIRST;

# EMPNO ENAME JOB       SAL TT_START   TT_END VT_START   VT_END     DT_START   DT_END
-- ----- ----- ------- ----- -------- -------- ---------- ---------- ---------- ----------
 1  7788 SCOTT ANALYST  3000  2366310  2366356
 2  7788 SCOTT ANALYST  3000  2366356  2366559            1990-01-01
 2  7788 Scott ANALYST  3000  2366356  2366408 1990-01-01
 3  7788 Scott ANALYST  3000  2366408  2366559 1990-01-01 1991-04-01
 4  7788 Scott ANALYST  3000  2366424  2366559 1991-10-01
 5  7788 SCOTT ANALYST  3000  2366559                     1989-01-01
 5  7788 SCOTT Analyst  3000  2366559          1989-01-01 1990-01-01
 5  7788 Scott Analyst  3000  2366559          1990-01-01 1991-04-01
 5  7788 Scott Analyst  3000  2366559  2366670 1991-10-01
 6  7788 Scott Analyst  3000  2366670          1991-10-01                       2013-03-24
 6  7788 Scott Analyst  3000  2366670          1991-10-01 2014-01-01 2013-03-24
 6  7788 Scott Manager  6000  2366670          2014-01-01            2013-03-24" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQL&gt; SELECT dense_rank() OVER(ORDER BY versions_startscn) event_no, empno, ename, job,</span></span>
<span class="line"><span style="color: #D4D4D4">  2         sal, versions_startscn tt_start, versions_endscn tt_end,</span></span>
<span class="line"><span style="color: #D4D4D4">  3         to_char(vt_start,&#39;YYYY-MM-DD&#39;) vt_start, to_char(vt_end,&#39;YYYY-MM-DD&#39;) vt_end,</span></span>
<span class="line"><span style="color: #D4D4D4">  4         to_CHAR(dt_start,&#39;YYYY-MM-DD&#39;) dt_start, to_char(dt_end,&#39;YYYY-MM-DD&#39;) dt_end</span></span>
<span class="line"><span style="color: #D4D4D4">  5    FROM empv VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE</span></span>
<span class="line"><span style="color: #D4D4D4">  6   WHERE empno = 7788 AND versions_operation IN (&#39;I&#39;,&#39;U&#39;)</span></span>
<span class="line"><span style="color: #D4D4D4">  7   ORDER BY tt_start, vt_start NULLS FIRST, dt_start NULLS FIRST;</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4"># EMPNO ENAME JOB       SAL TT_START   TT_END VT_START   VT_END     DT_START   DT_END</span></span>
<span class="line"><span style="color: #D4D4D4">-- ----- ----- ------- ----- -------- -------- ---------- ---------- ---------- ----------</span></span>
<span class="line"><span style="color: #D4D4D4"> 1  7788 SCOTT ANALYST  3000  2366310  2366356</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4"> 2  7788 SCOTT ANALYST  3000  2366356  2366559            1990-01-01</span></span>
<span class="line"><span style="color: #D4D4D4"> 2  7788 Scott ANALYST  3000  2366356  2366408 1990-01-01</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4"> 3  7788 Scott ANALYST  3000  2366408  2366559 1990-01-01 1991-04-01</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4"> 4  7788 Scott ANALYST  3000  2366424  2366559 1991-10-01</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4"> 5  7788 SCOTT ANALYST  3000  2366559                     1989-01-01</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4"> 5  7788 SCOTT Analyst  3000  2366559          1989-01-01 1990-01-01</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4"> 5  7788 Scott Analyst  3000  2366559          1990-01-01 1991-04-01</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4"> 5  7788 Scott Analyst  3000  2366559  2366670 1991-10-01</span></span>
<span class="line"><span style="color: #D4D4D4"> 6  7788 Scott Analyst  3000  2366670          1991-10-01                       2013-03-24</span></span>
<span class="line"><span style="color: #D4D4D4"> 6  7788 Scott Analyst  3000  2366670          1991-10-01 2014-01-01 2013-03-24</span></span>
<span class="line"><span style="color: #D4D4D4"> 6  7788 Scott Manager  6000  2366670          2014-01-01            2013-03-24</span></span></code></pre></div>



<p>7 rows have been changed or added based on event #5 at the transaction time 2366559. It clearly shows that DML operations in a temporal model are not trivial. All the more support in that area for VT and DT is missed.</p>



<p>The next query filters the data for Scott on the transaction time (SYSDATE=default), valid time (2014-01-01) and decision time (2013-04-01). This way the result is reduced exactly to a single row.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">7) Point-in-time Query</span><span role="button" tabindex="0" data-code="SQL&gt; SELECT empno, ename, job, sal,
  2         to_char(vt_start,'YYYY-MM-DD') AS vt_start,
  3         to_char(vt_end,'YYYY-MM-DD') AS vt_end,
  4         to_CHAR(dt_start,'YYYY-MM-DD') AS dt_start,
  5         to_char(dt_end,'YYYY-MM-DD') AS dt_end
  6    FROM empv AS OF period FOR dt DATE '2013-04-01'
  7   WHERE empno = 7788 AND
  8         (vt_start <= DATE '2014-01-01' OR vt_start IS NULL) AND
  9         (vt_end &gt; DATE '2014-01-01' OR vt_end IS NULL)
 10   ORDER BY vt_start NULLS FIRST, dt_start NULLS FIRST;

EMPNO ENAME JOB       SAL VT_START   VT_END     DT_START   DT_END
----- ----- ------- ----- ---------- ---------- ---------- ----------
 7788 Scott Manager  6000 2014-01-01            2013-03-24" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQL&gt; SELECT empno, ename, job, sal,</span></span>
<span class="line"><span style="color: #D4D4D4">  2         to_char(vt_start,&#39;YYYY-MM-DD&#39;) AS vt_start,</span></span>
<span class="line"><span style="color: #D4D4D4">  3         to_char(vt_end,&#39;YYYY-MM-DD&#39;) AS vt_end,</span></span>
<span class="line"><span style="color: #D4D4D4">  4         to_CHAR(dt_start,&#39;YYYY-MM-DD&#39;) AS dt_start,</span></span>
<span class="line"><span style="color: #D4D4D4">  5         to_char(dt_end,&#39;YYYY-MM-DD&#39;) AS dt_end</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  6    FROM empv AS OF period FOR dt DATE &#39;2013-04-01&#39;</span></span>
<span class="line"><span style="color: #D4D4D4">  7   WHERE empno = 7788 AND</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  8         (vt_start &lt;= DATE &#39;2014-01-01&#39; OR vt_start IS NULL) AND</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  9         (vt_end &gt; DATE &#39;2014-01-01&#39; OR vt_end IS NULL)</span></span>
<span class="line"><span style="color: #D4D4D4"> 10   ORDER BY vt_start NULLS FIRST, dt_start NULLS FIRST;</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">EMPNO ENAME JOB       SAL VT_START   VT_END     DT_START   DT_END</span></span>
<span class="line"><span style="color: #D4D4D4">----- ----- ------- ----- ---------- ---------- ---------- ----------</span></span>
<span class="line"><span style="color: #D4D4D4"> 7788 Scott Manager  6000 2014-01-01            2013-03-24</span></span></code></pre></div>



<p>Queries on multi-temporal data are relatively simple if all time periods are filtered at a point in time. The AS OF PERIOD clause (for DT) simplifies the query, but the complexity of a traditional WHERE condition (for VT) is not much higher.</p>



<p><span style="font-size: 1.5em; line-height: 1.5em;">Conclusion</span></p>



<p>The support for temporal data management in Oracle 12c is based on sound concepts, but the implementation is currently incomplete. I miss mainly a temporal DML API, temporal integrity constraints, temporal joins and temporal aggregations. I recommend using Oracle&#8217;s semantics for periods (half-open intervals, NULL for +/- infinity)&nbsp;in existing models, to simplify the migration to Temporal Validity.</p>



<p>In the real world, we use a lot of temporal dimensions, consciously or unconsciously at the same time. However, in data models, every additional temporal dimension increases the complexity significantly. Data models are simplifications of the real world, based on requirements and a limited budget. I do not recommend using bi-temporality or even multi-temporality as a universal design pattern. Quite the contrary I recommend determining and documenting the reason for a temporal dimension per entity to ensure that temporal dimensions are used consciously&nbsp;and not modeled unnecessarily.</p>



<p>Oracle&#8217;s Flashback Data Archive is good, transparent and since Oracle 11.2.0.4 also a cost-free option to implement requirements regarding the transaction time. For all other time dimensions such as the valid time and the decision time I recommend using standardized tooling to apply DML to temporal data.</p>



<p><em>Last update on 2015-10-24, amendments to match limitations of Oracle version 12.1.0.2.4.<br /></em></p>
<p>The post <a href="https://www.salvis.com/blog/2014/01/04/multi-temporal-database-features-in-oracle-12c/">Multi-temporal Features in Oracle 12c</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.salvis.com/blog/2014/01/04/multi-temporal-database-features-in-oracle-12c/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
	</channel>
</rss>
