Skip to main content

Command Palette

Search for a command to run...

Why SQLcl Runs Your Script Twice: The "Slash" That Breaks Oracle APEX Deployments

Updated
5 min read

Recently, I was working on a project where my responsibility was straightforward on paper: prepare a clean database installation script and hand it over to the DBA team for production deployment. The script included all the required database objects—tables, supporting structures, and a few setup pieces that an Oracle APEX application depended on. Nothing unusual.

Before handing it over, I did what most of us do: I ran the script myself using SQLcl.

That’s when things went sideways.

Halfway through the run, Oracle started complaining that objects already existed ( ORA-00955: name is already used by an existing object). Tables that should have been created for the first time were suddenly reported as already existing. I checked the schema—nothing there. I went back to the script and reviewed it carefully. It was a straightforward script, and I hadn’t added the CREATE statement twice for any table. There were no parallel sessions and no re-runs. It was one script, executed once, yet Oracle behaved as if it had already seen it before. So why did Oracle try to create it twice?

I then ran the same script from SQL Developer, and it executed without any issues. The error only appeared when I ran it from SQLcl. At that point, it was clear the problem wasn’t the table definition or the data. Something about how SQLcl was executing the script was causing the same statement to run more than once.

That is when I noticed how the script was written. The deployment script contained a mix of objects—SQL statements like CREATE TABLE, CREATE INDEX, and CREATE TRIGGER, along with PL/SQL blocks such as packages, procedures, and functions. In these scripts, we often use semicolons and / to terminate statements, and that is where the problem started. Using / after statements that were already terminated with a semicolon caused SQLcl to execute them again.

How SQLcl Actually Executes Your Script

To understand why the same statement runs twice, you need to understand how SQLcl executes commands. SQLcl keeps track of the last completed SQL or PL/SQL statement in an internal buffer and decides when to execute it based on specific markers.

The execution rules:

  • A semicolon (;) ends a SQL statement. When SQLcl sees it, the statement is immediately sent to the database and executed.

  • A slash (/) does not end a statement. It tells SQLcl to execute the most recently parsed SQL or PL/SQL unit stored in its buffer.

How SQLcl Executes SQL vs PL/SQL

SQLcl treats SQL statements and PL/SQL blocks differently. That distinction is the root of this entire issue.

Plain SQL statements (DDL / DML)

For SQL statements like CREATE TABLE, CREATE INDEX, INSERT, or UPDATE, the semicolon (;) is enough.

CREATE TABLE customers (
    id NUMBER PRIMARY KEY,
    first_name VARCHAR2(4000),
    last_name  VARCHAR2(4000)
);

What happens:

  • SQLcl reads up to the semicolon.

  • The statement is immediately sent to the database and executed.

  • The statement remains as the “last parsed statement” in SQLcl’s buffer.

PL/SQL blocks (procedures, functions, packages, anonymous blocks)

PL/SQL works differently. The semicolon only ends individual statements inside the block—it does not execute the block itself.

BEGIN
    INSERT INTO apex_log_util (id, log_msg)
    VALUES (1, 'Init complete');
END;

At this point:

  • SQLcl knows the block is complete.

  • But it does not execute it yet.

Execution only happens when SQLcl sees a slash on a new line:

BEGIN
    INSERT INTO apex_log_util (id, log_msg)
    VALUES (1, 'Init complete');
END;
/

What happens:

  • The slash tells SQLcl to execute the completed PL/SQL block stored in the buffer.

  • The block runs exactly once.

The Flow That Causes Double Execution

This is where things go wrong. If you write the following in a script:

CREATE TABLE customers (
    id NUMBER PRIMARY KEY,
    first_name VARCHAR2(4000),
    last_name  VARCHAR2(4000)
);
/

Here’s what SQLcl does:

  • SQLcl reads up to the semicolon and executes the CREATE TABLE statement immediately. That’s the first execution, and it succeeds.

  • SQLcl then sees the / on the next line. The slash tells SQLcl to re-execute whatever is currently in its buffer.

  • The buffer still contains the CREATE TABLE statement, so SQLcl sends it to the database again.

  • The second execution fails with ORA-00955: name is already used by an existing object.

This is why using / after SQL statements that already end with a semicolon causes objects to be executed twice—and why this issue only shows up in tools like SQLcl or SQL*Plus, not in SQL Developer.

Why Mixing Them Causes Trouble

In deployment scripts—especially for APEX—you usually have both:

  • SQL statements (tables, indexes, triggers)

  • PL/SQL blocks (packages, procedures, setup logic)

Because / is required for PL/SQL, developers often use it everywhere for consistency. That’s where the problem starts.

If you use / after:

  • PL/SQL blocks → correct

  • SQL statements already ending with ; → double execution

The Clean Scripting Standard

If you want your deployment scripts to be bulletproof stick to these two patterns:

1. Pure SQL (Tables, Indexes, Views)

Use the semicolon only. No slash.

-- Correct for SQL
CREATE INDEX idx_emp_dept ON emp(deptno);

-- No slash here!

2. PL/SQL (Packages, Procedures, Functions)

Use the semicolon for internal logic, and a single slash on a new line to execute.

-- Correct for PL/SQL
BEGIN
    apex_instance_admin.set_parameter('SELF_SERVICE_SIGN_UP_YN', 'N');
END;
/

Final Takeaway

The / character isn’t syntax. It’s a command. In SQLcl, it means “run that again.” If you use it carelessly, SQLcl will do exactly what you asked—even if that means creating the same object twice.

Once you understand how SQLcl actually processes scripts, these deployment issues stop being mysterious and start being avoidable. And in production deployments, predictable behavior is far more valuable than habit or convenience.