<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Oracle Coding Tips With Jamie]]></title><description><![CDATA[Oracle Coding Tips With Jamie]]></description><link>https://oracle-coding-tips.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Oracle Coding Tips With Jamie</title><link>https://oracle-coding-tips.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 16 Oct 2026 07:18:56 GMT</lastBuildDate><atom:link href="https://oracle-coding-tips.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Oracle SQL Syntax Improvements]]></title><description><![CDATA[Oracle has been busy making a number of syntax changes in recent versions of Oracle. This blog aims to be a one stop shop for a few of the best ones (in my opinion).
No More Dual


Ok, that's Duel rat]]></description><link>https://oracle-coding-tips.hashnode.dev/oracle-sql-syntax-improvements</link><guid isPermaLink="true">https://oracle-coding-tips.hashnode.dev/oracle-sql-syntax-improvements</guid><dc:creator><![CDATA[jamiemsutcliffe]]></dc:creator><pubDate>Fri, 09 Oct 2026 08:06:28 GMT</pubDate><content:encoded><![CDATA[<p>Oracle has been busy making a number of syntax changes in recent versions of Oracle. This blog aims to be a one stop shop for a few of the best ones (in my opinion).</p>
<h3>No More Dual</h3>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/d79fdc21-a169-48a1-b49a-521c320524fb.jpg" alt="" style="display:block;margin:0 auto" />

<p>Ok, that's Duel rather than Dual (and only oldies will get it anyway). Moving on...</p>
<p>Since version 23c Oracle has made it no longer necessary to do this:</p>
<pre><code class="language-sql">SELECT SYSDATE
FROM DUAL;
</code></pre>
<p>Instead you can just do this:</p>
<pre><code class="language-sql">SELECT SYSDATE;
</code></pre>
<p>This can look a little bit strange till you get used to it but the above queries are equivalent. A couple of other examples without FROM DUAL:</p>
<pre><code class="language-sql">SELECT 1
UNION ALL
SELECT 2;
</code></pre>
<pre><code class="language-sql">SELECT LEVEL CONNECT BY ROWNUM &lt;= 4;
</code></pre>
<h3>Direct Joins in Update and Delete Statements</h3>
<p>You can now (v23ai/26ai) directly join tables within updates and deletes. These are more readable than using subqueries:</p>
<pre><code class="language-sql">UPDATE EMP E
SET E.SAL = E.SAL + (E.SAL * .1)
FROM DEPT D
WHERE E.DEPTNO = D.DEPTNO
  AND D.LOC = 'YORKSHIRE';
</code></pre>
<p>Or this:</p>
<pre><code class="language-sql">DELETE FROM EMP E
FROM DEPT D
WHERE E.DEPTNO = D.DEPTNO
  AND D.DNAME = 'RESEARCH';
</code></pre>
<h3>Insert Improvements</h3>
<p>The SET clause - INSERT statements like UPDATE statements...</p>
<p>The following:</p>
<pre><code class="language-sql">INSERT INTO CLAIMS (ID, CREATED_DT, CREATED_BY, RECEIVED_DT, STATUS)
VALUES
(CLAIM_SEQ.NEXTVAL, SYSDATE, USER, :1, :2);
</code></pre>
<p>Can be replaced by:</p>
<pre><code class="language-sql">INSERT INTO CLAIMS 
SET ID = CLAIM_SEQ.NEXTVAL, CREATED_DT=SYDATE, CREATED_BY=USER RECEIVED_DT=:1, STATUS=:2;
</code></pre>
<p>The SET clause is much easier for column to value alignment making them more readable and far less likely to set any columns to the wrong value.</p>
<p>Oracle also supports multirow inserts like these:</p>
<pre><code class="language-sql">INSERT INTO CLAIMS (ID, CREATED_DT, CREATED_BY, RECEIVED_DT, STATUS)
VALUES
(CLAIM_SEQ.NEXTVAL, SYSDATE, USER, :1, :2),
(CLAIM_SEQ.NEXTVAL, SYSDATE, USER, :3, :4);
</code></pre>
<p>And (with SET):</p>
<pre><code class="language-sql">INSERT INTO CLAIMS 
SET (ID = CLAIM_SEQ.NEXTVAL, CREATED_DT=SYDATE, CREATED_BY=USER RECEIVED_DT=:1, STATUS=:2), (ID = CLAIM_SEQ.NEXTVAL, CREATED_DT=SYDATE, CREATED_BY=USER RECEIVED_DT=:3, STATUS=:4);
</code></pre>
<p>Note the use of parenthesis here to divide the rows.</p>
<p>I was curious to see if it was possible to use the returning clause returning into a collection e.g. RETURNING ID BULK COLLECT INTO r_ids; but this was not possible for either of the above inserts and resulting in the following errors respectively:</p>
<blockquote>
<p>ORA-63853: RETURNING clause is not allowed with a multi-row INSERT INTO SET clause.</p>
</blockquote>
<blockquote>
<p>ORA-63809: returning clause is not allowed with INSERT and Table Value Constructor</p>
</blockquote>
<hr />
<p>Follow my blog and <a href="https://www.youtube.com/channel/UC_KKG03SmGysrr7eEDgJ2bQ">YouTube Channel</a> for more Oracle coding tips.</p>
]]></content:encoded></item><item><title><![CDATA[The Future has Arrived]]></title><description><![CDATA[The rate of change as a software developer recently has been unprecedented in my quarter of a century career. I remember at various times obsessing over brand new features of the Oracle DB in the Orac]]></description><link>https://oracle-coding-tips.hashnode.dev/the-future-has-arrived</link><guid isPermaLink="true">https://oracle-coding-tips.hashnode.dev/the-future-has-arrived</guid><dc:creator><![CDATA[jamiemsutcliffe]]></dc:creator><pubDate>Wed, 23 Sep 2026 07:16:17 GMT</pubDate><content:encoded><![CDATA[<p>The rate of change as a software developer recently has been unprecedented in my quarter of a century career. I remember at various times obsessing over brand new features of the Oracle DB in the Oracle Magazine (a printed publication no less) such as the returning clause, results cache and finer grained dependencies. Unfortunately DB upgrades were always few and far between with workplace on-premise solutions, often requiring the halting of ongoing project work and a lot of coordination between various teams and down time etc. While you waited though, you could always pre-emptively comment the new features into your code for when they became available, great. It is only in recent times when I have had the luxury of working on a modern autonomous database which is managed by Oracle.</p>
<h3>Back to the Future</h3>
<p>I recently had a week off work, when I came back Github Copilot had arrived. Two days later we had upgraded to Apex 26.4 on our development environment. I have to say it is a very exciting time and it feels for me to be an important precipice where there is so much to make use of and discover. There have always been blockers and effort involved in upgrading the toolset but with Oracle Apex and Autonomous databases these are minimised so the benefits can be experienced without the associated rigmarole.</p>
<h3>Initial Observations on the Use of AI</h3>
<p>I have had recent experiences of searching for coding solutions via a search engine and have had reasonable success in some cases. I have encountered some things for the first time:</p>
<ul>
<li><p>AI providing javascript commands to be run on the command line in order to debug why something is failing. AI can be very confident on prescribing solutions though and I have witnessed upwards of ten false observations. (This is happening because of x, y, z - run this script to prove this...) - wrong.</p>
</li>
<li><p>Code completion that does language corrections (expected) but also provided meaningful descriptive text based on a code and some associated letter text. This behaviour and similar behaviours will improve the solution being offered and make me wonder if this can streamline the requirements gathering process.</p>
</li>
<li><p>Installing software on my behalf (it did ask me first!)</p>
</li>
<li><p>Creating a new Apex page based on some prompts... Not just the page but supporting objects such as a view and a package. It even checked the application to adhere to existing patterns in our application. It didn't work flawlessly but was a great starting point.</p>
</li>
</ul>
<p>The use of Github Copilot has impressed me - not only does it create pages and components it also inspects the apex application to make sure the solution adheres to existing patterns! When trying to add Oracle Apex and DB skills it was even uploading software onto my laptop.</p>
<h3>How Good Might AI Get?</h3>
<p>Imagine having access to a team of the best developers in the world at your fingertips. As coders we often work alone (peer reviews aside) and some of us can be considered experts in specific areas. What if we were experts in everything? What if we could automatically access every relevant reference and test our code as it was written. Multiply this by a million.</p>
<h3>If We're not Coding then what are we going to be doing?</h3>
<p>Although AI is going to outshine human coders it is only going to be able to make the solution we tell it to. Therefore our skills are less about typing every line of code and manually building screens but more about being good at telling AI exactly what is required and iteratively checking the result. At the moment I've been using AI to provide a starting point and then finishing this solution off manually, as I go round this process a number of times I will have built up some guidelines and possibly a library of prompts. It is also doable to provide coding standards that AI must follow. Basically we're the human in the loop, we have to build into our prompts guidance so that AI cares about the outcome.</p>
<h3>Exciting Times</h3>
<p>Another reason why right now is so exciting is that Oracle Apex has just become AI enabled but the models using it are currently lacking in specific skills and knowledge. As this learning happens we are going to see an exponential growth in the abilities of these coding agents.</p>
<h3>What Will Happen When Users are No Longer Human?</h3>
<p>As applications can be written by AI it seems likely that AI be writing programs to be used by AI. I say programs rather than applications as there may not be a front end at that point other than a limited application for humans to look at data/reporting and perhaps admin screens to govern the AI functionality. There could even be functionality created as it is needed. As a simple example - AI encounters some data items that are as yet not captured in the system. The relevant agents amend the back end tables and amend the relevant packages so that the new data can be added. This could happen with barely skipping a beat.</p>
]]></content:encoded></item><item><title><![CDATA[Oracle OCI - The Naked Truth - Post 2]]></title><description><![CDATA[In order to migrate data and process the documents I broke the solution into these steps:
Whilst the OCI client was being set up I worked on the solution which included code to do the following:

An i]]></description><link>https://oracle-coding-tips.hashnode.dev/oracle-oci-the-naked-truth-post-2</link><guid isPermaLink="true">https://oracle-coding-tips.hashnode.dev/oracle-oci-the-naked-truth-post-2</guid><dc:creator><![CDATA[jamiemsutcliffe]]></dc:creator><pubDate>Mon, 14 Sep 2026 07:24:10 GMT</pubDate><content:encoded><![CDATA[<p>In order to migrate data and process the documents I broke the solution into these steps:</p>
<p>Whilst the OCI client was being set up I worked on the solution which included code to do the following:</p>
<ul>
<li><p>An initial load of client data from a spreadsheet to store the documents against.</p>
</li>
<li><p>Get documents from the object storage staging area.</p>
</li>
<li><p>Load them into the DB into a documents staging table as blobs/filenames etc. on the DB.</p>
</li>
<li><p>Reconcile the data (needed due to a previous load of some of the files/client data).</p>
</li>
<li><p>Load the documents to object storage storing them in the application documents table (docs named as a uuid held in data).</p>
</li>
<li><p>Delete the blobs from the staging tables.</p>
</li>
</ul>
<p>I developed the solution and loaded a sample of documents against a small no. of clients as a proof of the solution. To do this I loaded files into the bucket manually via the OCI web application. Once this was done I thought I'd cracked it...</p>
<p>However, when I first tried to run the package procedure to load the full list of documents into the document staging table I found that it did not run in a timely manner. I left it running for 6 hours before killing the process...</p>
<p>This was my original program unit:</p>
<pre><code class="language-sql">procedure load_oci_files (p_input_id number) is
begin
    FOR rec IN (
        SELECT *
        FROM
        (
            SELECT object_name, file_name, ROWNUM rn
            FROM
                (
                SELECT object_name /*substr/replace*/ file_name,
                       object_name            
                FROM   DBMS_CLOUD.LIST_OBJECTS(credential_name =&gt;                   
                       'OCI_OBJ_STORAGE',
                       location_uri =&gt;
                       g_base_url||g_ra_micro_bucket)
                WHERE BYTES &gt; 0
                ORDER BY object_name
                )
            WHERE FILE_NAME NOT IN ('lfdNr', 'Thumbs.db')
        )  
        WHERE rn BETWEEN (p_Input_id*10000)-9999 AND p_Input_id*10000 
        )  
    LOOP
        l_Blob := DBMS_CLOUD.GET_OBJECT(
                  credential_name =&gt; 'MY_CRED_NAME', -- pre-created credential
                  object_uri      =&gt; rec.object_uri
                                        );
  
      insert into DOCUMENT_FILE
        (
        ...
        FSIZE,
        FCONTENT,
        ...
        )
        values
        (
        ...
        dbms_lob.getlength(l_Blob),
        l_Blob,
        ...
        );        
      END LOOP;
end load_oci_files;
</code></pre>
<h3>Parallel Processing</h3>
<p>I looked into how I could speed up the program unit and looked into parallel processing (a first for me). Basically parallel processing allows a task to be split and run in parallel by a number of 'workers'. It is generally table based and works by splitting the load down by either row_id or a number (e.g. id column). Once it is set up then the handling of managing the split of ids and parallel processing jobs etc. is managed by Oracle.</p>
<p>I had a ‘conversation’ with AI via a popular search engine which recommended the code. I reworked this into my package whilst also verifying and learning from this page: <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_PARALLEL_EXECUTE.html#GUID-18C01BED-25F5-4B87-BA77-170EDFB70423">DBMS_PARALLEL_EXECUTE</a>. I added a separate program unit to load the filenames and URIs into a STAGING_LOAD_DOCS table, to allow the ROWID split, and the remaining code now looked like this:</p>
<pre><code class="language-sql">function load_oci_files_sql (p_input_id number) return clob is
  l_sql CLOB;
BEGIN
  l_sql := q'[
    DECLARE
      l_blob blob;
    BEGIN
      FOR rec IN (
        SELECT input_id, object_uri, file_name, object_name
        FROM STAGING_LOAD_DOCS
        WHERE ROWID BETWEEN :start_id AND :end_id
          AND input_id = ]'||p_input_id||q'[
      ) LOOP
        l_Blob := DBMS_CLOUD.GET_OBJECT(
                      credential_name =&gt; 'MY_CRED_NAME', -- pre-created credential
                      object_uri      =&gt; rec.object_uri
                                        );
      insert into DOCUMENT_FILE
        (
        ...
        FSIZE,
        FCONTENT,
        ...
        )
        values
        (
        ...
        dbms_lob.getlength(l_Blob),
        l_Blob,
        ...
        );        
      END LOOP;
     
    END;
  ]';

  return l_sql;
end load_oci_files_sql;

procedure create_and_run_load_task(p_input_id number) is
    l_sql CLOB;
BEGIN

    -- Drop if exists to avoid wrong state
    BEGIN
        DBMS_PARALLEL_EXECUTE.DROP_TASK(k_Task_Name);
        EXCEPTION
        WHEN OTHERS THEN NULL; -- Ignore if task doesn't exist
    END;

    DBMS_PARALLEL_EXECUTE.CREATE_TASK(k_Task_Name);

    -- Chunk by ROWID so each chunk processes a subset of rows
    DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_ROWID(
        task_name   =&gt; k_Task_Name,
        table_owner =&gt; USER,
        table_name  =&gt; k_Task_Name,
        by_row      =&gt; TRUE,
        chunk_size  =&gt; 1  -- one file per chunk
    );

  l_sql := load_oci_files_sql(p_input_id);

  DBMS_PARALLEL_EXECUTE.RUN_TASK(
    task_name      =&gt; k_Task_Name,
    sql_stmt       =&gt; l_sql,
    language_flag  =&gt; DBMS_SQL.NATIVE,
    parallel_level =&gt; 4  -- number of concurrent workers
  );
END create_and_run_load_task;
</code></pre>
<hr />
<p>The key sections in the code are this line in the select statement:</p>
<pre><code class="language-sql">WHERE ROWID BETWEEN :start_id AND :end_id
</code></pre>
<p>And also these parameters: for example:<br /><code>chunk_size =&gt; 100 -- 100 files per chunk</code><br /><code>parallel_level =&gt; 8 -- number of concurrent workers</code></p>
<p>These are used to split the load into multiple sections. E.g. for this example:</p>
<p>8 workers in parallel will process 100 rows each.</p>
<h2>Lessons Learnt and Outstanding Questions</h2>
<p>AI suggested a chunk size of 1 which seemed extreme so I asked AI to explain this. The reasoning was that the dbms_cloud.get_object call and inserting the blobs was the most expensive operation. Also it mentioned that this would give the least lag time compared to grouping a set number of different file types.</p>
<p>BUT when I re-examined this whilst writing this blog post then I was given findings to the contrary:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/93521a0c-6dfd-4748-a176-0a1dfa74d0a2.png" alt="" style="display:block;margin:0 auto" />

<p>In my case I proceeded with a chunk size of 1.</p>
<p>Upon further reading about DBMS_PARALLEL_EXECUTE I've realised that the examples are primarily for sql update statements (inserts and merges are also mentioned) and that the criteria are focused on this. Indeed the DBMS_PARALLEL_EXECUTE.RUN_TASK parameter is sql_stmt. PL/SQL code can be used though, clearly so I am interested to learn more about this.</p>
<p>Another point is around error handling - the following code (the WHILE loop) is in the examples:</p>
<pre><code class="language-sql">L_status := DBMS_PARALLEL_EXECUTE.TASK_STATUS('mytask'); WHILE(l_try &lt; 2 and L_status != DBMS_PARALLEL_EXECUTE.FINISHED) LOOP 
  L_try := l_try + 1; DBMS_PARALLEL_EXECUTE.RESUME_TASK('mytask');
  L_status := DBMS_PARALLEL_EXECUTE.TASK_STATUS('mytask'); 
END LOOP;
</code></pre>
<p>In my case had no such error handling in the code but this was an overseen, one off process of running the scripts for the migration and I was running the following to check for errors (there were none).</p>
<pre><code class="language-sql">SELECT *
FROM   user_parallel_execute_chunks
WHERE  task_name = 'RA_MICRO_LOAD_DOCS'
ORDER BY chunk_id;
</code></pre>
<p>In the case of reusable production code then the error handling is likely to be essential.</p>
<h2>Use of AI in Coding Solutions</h2>
<p>As mentioned earlier, I have been partially assisted by AI (via an internet search engine in this case) in providing this solution. There are some obvious pitfalls to this and likely some non-obvious ones too. In closing this blog post here are some of my initial concerns (although I'm barely scratching the surface - more to come on this topic):</p>
<ul>
<li><p>Not fully understanding the solution provided by AI (in reality this is going to happen regularly).</p>
</li>
<li><p>AI not understanding the problem, the problem being miscommunicated and/or the solution being incomplete. Solutions should be thoroughly tested.</p>
</li>
<li><p>With normal, manually typed code the practice of thinking about and writing the code means the code is more likely to be understood by the programmer. AI provided code means there is a layer of separation between the programmer and the solution.</p>
</li>
<li><p>As the use of AI coding becomes more widespread us coders are inevitably going to have diminished skills in writing code although code reading and diagnostic skills are still a strong requirement currently.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Oracle APEX Working Copy Restore]]></title><description><![CDATA[If you're using APEX working copies then you may have encountered your working copy disappearing. It can happen due to someone reinstalling the main application, due to a database refresh, someone acc]]></description><link>https://oracle-coding-tips.hashnode.dev/oracle-apex-working-copy-restore</link><guid isPermaLink="true">https://oracle-coding-tips.hashnode.dev/oracle-apex-working-copy-restore</guid><dc:creator><![CDATA[jamiemsutcliffe]]></dc:creator><pubDate>Tue, 08 Sep 2026 19:08:50 GMT</pubDate><content:encoded><![CDATA[<p>If you're using APEX working copies then you may have encountered your working copy disappearing. It can happen due to someone reinstalling the main application, due to a database refresh, someone accidentally deleting the wrong working copy or a myriad of other reasons. It can be very worrying if you have spent a long time working on your copy and it's apparently lost.</p>
<p>It is therefore sensible to routinely export your working copy application so it can be refreshed from the export... BUT if your working copy has gone and you have not exported then, fear not, you may be able to restore from a backup.</p>
<h3>How to Restore Your Working Copy From a Backup</h3>
<p>Go to 'Workspace Utilities' &gt; 'All Workspace Utilities'</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/b5de103f-fa9b-49d5-8785-15ef52839d54.png" alt="" style="display:block;margin:0 auto" />

<p>Go to 'Manage Backups'</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/88ea92a8-42ac-46af-a396-bd506e247143.png" alt="" style="display:block;margin:0 auto" />

<p>Click the working copy you want to restore</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/4df0a576-4c87-4cec-8788-f29bda3902f4.png" alt="" style="display:block;margin:0 auto" />

<p>For the working copy you want to restore (likely the latest) click the 'Action' drop down and select 'Download'. Note: you cannot 'Restore' the working copy directly as it no longer exists.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/fee6198e-9ee7-48ba-b86e-a8711025a227.png" alt="" style="display:block;margin:0 auto" />

<p>Save the zip file then open the file location and extract the file</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/3cfee123-73e8-4407-ac96-0597395c8981.png" alt="" style="display:block;margin:0 auto" />

<p>Go the main application and click 'Create Working Copy'</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/5da82ece-a1ce-45b1-bf3f-d1731b95f1e6.png" alt="" style="display:block;margin:0 auto" />

<p>Give the working copy any name e.g. WC1 - it will be replaced when we restore the working copy. Click 'Create Working Copy'</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/d34ecdbc-7d39-45d5-aa16-54e8a1951b39.png" alt="" style="display:block;margin:0 auto" />

<p>You will now be in your new working copy. Make a note of the application id - (112 in this example)</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/036ff3c2-2302-47be-bf66-42c6ebb8876f.png" alt="" style="display:block;margin:0 auto" />

<p>In your new working copy select 'Export / Import'</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/35ca0aa8-3238-4e67-96f7-50f9b40e07a4.png" alt="" style="display:block;margin:0 auto" />

<p>On the following screen select 'Import' and then drag and drop or select the application being restored. Click 'Next'</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/b0cf9c04-80b0-4a1a-9b7f-ae0353cf2b96.png" alt="" style="display:block;margin:0 auto" />

<p>For 'Import As Application' select 'Change Application ID' and set the 'New Application ID' to match the working copy application id. Click 'Import Application'.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/33c5ff35-3920-409e-9d54-fa0c629a6d21.png" alt="" style="display:block;margin:0 auto" />

<p>Your working copy should now have been successfully restored from the backup</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/8f335dcb-5677-40ed-aaaf-1985dfb1a1a3.jpg" alt="" style="display:block;margin:0 auto" />]]></content:encoded></item><item><title><![CDATA[Oracle OCI - The Naked Truth]]></title><description><![CDATA[Blog Post 1 - OCI Client Set Up and Bulk Load Commands

Introduction
I often think when developing software that the maximum learning happens when there are challenges or misunderstanding with how som]]></description><link>https://oracle-coding-tips.hashnode.dev/oracle-oci-the-naked-truth</link><guid isPermaLink="true">https://oracle-coding-tips.hashnode.dev/oracle-oci-the-naked-truth</guid><dc:creator><![CDATA[jamiemsutcliffe]]></dc:creator><pubDate>Sat, 29 Aug 2026 13:07:56 GMT</pubDate><content:encoded><![CDATA[<p>Blog Post 1 - OCI Client Set Up and Bulk Load Commands</p>
<p><a href="https://ace.oracle.com/ords/ace/oapt/profile/Jam"><img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/477a1e2f-9ba0-47bc-87b0-15136ff204dd.png" alt="Jamie Sutcliffe" style="display:block;margin-left:auto" /></a></p>
<h3>Introduction</h3>
<p>I often think when developing software that the maximum learning happens when there are challenges or misunderstanding with how something works. I have at some points probably made my fair share of mistakes but I always try to learn from them so the same mistake repeated is rare. Here is my account of a recent project which will be useful as a reference for object storage, OCI client and associated coding including using parallel processing but also of interest for the insights of mistakes made and lessons learned – without them being glossed over, the naked truth!</p>
<h3>Initial Steps/Configuration</h3>
<p>I was tasked with loading files to object storage and then associating with data in our database. The accepted way to achieve this was via the OCI client being installed onto the server where the files resided. Then bulk load commands could be used to load the files from the command line into the OCI staging area. I needed to work with the service desk to get the OCI client installed. The first issue was due to restrictions on the server where the files resided prevented the OCI client from being installed. A copy of the server was generated and then the OCI client was installed.</p>
<p>Once the OCI client software had been installed I then set up the client on the server. I found some instructions that instructed of setting up a new user, group and policy but I reasoned that I could use my own user so I set up the ssh keys against my user and copied the private key to configure the OCI client on the server.</p>
<p>Some initial problems that were encountered with the OCI client were:</p>
<p>1/ OCI was installed but not recognised when running commands via the command line. E.g. <code>oci -i</code></p>
<p>The fix was to run:</p>
<p><code>set PATH=%PATH%;C:\OCI\bin</code></p>
<p>to add c:/OCI/bin to the path environment variable. Note: I could run this command myself (as a non-admin user) but I found I had to rerun it for subsequent log ons in order to use the OCI client.</p>
<p>2/ After the above was resolved the next error was "No Python" at... saying that Python wasn’t installed.</p>
<p>Once the OCI and Python were installed I could run oci commands successfully. Yes!</p>
<h3>Bulk Upload Commands</h3>
<p>I could then start to run the bulk commands. I initially started with this command from Lee Dixon's excellent blog: <a href="https://blog.cloudnueva.com/bulk-uploading-25-million-files-to-oci-object-storage">Bulk Uploading 25M Files to OCI Object Storage</a></p>
<p><code>oci os object bulk-upload -bn "ASCEND_INVOICES" --src-dir "H:\Ascend\M10" --overwrite --parallel-upload-count 500 --object-prefix "H/Ascend/M12/" --exclude *.Log &gt; H_M10.json</code></p>
<p>In my case the folder structures looked like this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/e8a4f86c-6bae-4518-9420-c4b238a26fdc.png" alt="" style="display:block;margin:0 auto" />

<p>The folders shown represent years, this was followed by month and days folders as follows:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/f7dbfe40-c80a-44ae-97f6-18c580be443d.png" alt="" style="display:block;margin:0 auto" />

<p><img src="align=%22center%22" alt="" /></p>
<p>All of the documents comprising of pdfs, docx and msg files were in the day folders (e.g. '19' in the above screenshot).</p>
<p>My initial command to upload files from the 17 folder was:</p>
<p><code>oci os object bulk-upload -bn "ra_micro_non_prod" --src-dir "X:\RA\ar\17" --overwrite --parallel-upload-count 500 &gt; ar_17.json</code></p>
<p>As (I reasoned) that I don’t need to <em><strong>change</strong></em> any paths I omitted the <code>object-prefix</code> parameter completely.</p>
<h3>Everything Seemed Rosie</h3>
<p>I then checked my resulting log files by navigating to them and checking the counts by searching a string and obtaining the count. (Lee Dixon has a better way of doing this from the command line but I went my own way). I then checked the ‘Approximate’ count on the server to ensure that the expected files were uploaded. Satisfied that the logs seemed to indicate everything had gone across and the OCI server count being approximate I assumed that all the files were loaded. Cue a rather optimistic and criminally low estimate of the remaining work when asked how the project was going by our CTO…</p>
<p>At this point I was feeling buoyant and surprised at how simple it all seemed… But at the time of giving my update I had lost sight certain key facts:</p>
<p>1/ I had done no load testing so didn’t actually know if my current solution would work successfully.</p>
<p>2/ Although there was no indication in the logs not all the files had been loaded (as transpired later).</p>
<h3>Silence Isn't Always Golden</h3>
<p>The issue I later uncovered was due to an assumption about the path parameter in the bulk load command. As the source directory was e.g. <code>--src-dir "X:\RA\ar\17"</code> then this uploaded to the OCI bucket as follows - starting at that folder point:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/315c9725-7a41-4139-9f56-76f9d634ad5f.png" alt="" style="display:block;margin:0 auto" />

<p>Note that as the top level structures weren't uploaded then all of the files from originating year and month folders e.g. 18/01, 19/01, 20/01 etc. were being loaded into the 01 folder... this was in effect silently removing all files with duplicate names. Looking at the bulk load command again:</p>
<p><code>oci os object bulk-upload -bn "ra_micro_non_prod" --src-dir "X:\RA\ar\17"</code> <strong>--overwrite</strong> <code>--parallel-upload-count 500 &gt; ar_17.json</code></p>
<p>The overwrite command means that any duplicates are not logged, only overwritten. On realising this the command became:</p>
<p><code>oci os object bulk-upload -bn "ra_micro" --src-dir "X:\RA\ar\17" --overwrite --parallel-upload-count 500 --object-prefix "ar/17/"&gt; ar_17.json</code></p>
<p>Note the object prefix parameter which is <strong>essential</strong> in this scenario to ensure that duplicates are not filtered out, especially when also using the overwrite command. With the above command the file paths were correctly represented in the OCI bucket (see below) and the file counts matched the source folders.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/5c6baad6-7cfe-4d3a-8ba9-74127f8df570.png" alt="" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/6a8b310b5f947d0eb679e4f5/06705eae-a119-47dc-81e1-7b2583f07c1e.png" alt="" style="display:block;margin:0 auto" />

<p>I used a query similar to this to check the files residing in the staging bucket:</p>
<p><code>SELECT * FROM DBMS_CLOUD.LIST_OBJECTS( credential_name =&gt; 'MY_CRED_NAME', location_uri =&gt; 'https://mybucketuri') WHERE BYTES &gt; 0 ORDER BY object_name;</code></p>
<p>This was checked against counts of files on the server (which I viewed by inspecting the folder properties on the windows server). The counts lined up perfectly now that the files were going into the correct folders.</p>
<p>Please join me, for more posts in this series as they become available.</p>
]]></content:encoded></item></channel></rss>