Power to Build

Home » Posts tagged 'SQL'

Tag Archives: SQL

DB – So, what’s a Transaction?


I posted a Gotcha in Oracle Commit some time back. This is one of the posts that gets visited a lot. Also, at work, I saw some SQL scripts that showed a complete lack of understanding of how transactions or commits work in Oracle (or any database for that matter). Hence this post.

Dictionary.com defines Transact as “To carry or conduct business to a conclusion or settlement”. And a Transaction is an act of Transacting! BusinessDictionary.com goes a bit further to explain it as a contract or agreement. It even goes further to define it as an event in accounting, such as entries that go into a cash register. A Transaction then is a self-contained unit of work, that is completed (either with success or failure). This is what is adapted in Database terminology. A transaction must be complete, reliable and independent of other actions.

In Database parlance, we call it an ACID – atomic, consistent, isolated and durable. See Microsoft’s post about ACID here or this Wikipedia article. When a DB transaction succeeds, we COMMIT it and when it fails, we ROLLBACK it.

Transactions in real life
A transaction typically happens between 2 parties and it typically concludes to the satisfaction of both the parties involved. The parties involved either agree to go all the way, or cancel/settle somehow. Think about it. Even if there are multiple parties involved, you can always break it down to smaller and smaller transaction until you can no longer split and that will among 2 parties.

A typical transaction we all can relate is the cash transaction at a bank (at a Teller counter the ATM): You give your card. The teller (or the machine) validates it and asks you to enter your pin number. Once you have done that successfully, then the teller (or the machine) asks you the type of transaction you would like to perform – deposit or withdraw. If you are withdrawing, you tell the amount and the teller gives you the money and deducts it from the account and finally gives you a receipt.

SOCIAL ATM

Courtesy life2point1 on toondoo.com

For example, imagine you are at an ATM depositing money into your account. Say you had $1000 in the account before you deposited another $100. This is your transaction with the bank at the moment. (You may have other transactions at the same bank and elsewhere, but each is an independent unit of work). If everything goes well, at the end of the “transaction”, your account will have $1100. What if all is not well? What if Power goes down in the middle of this transaction? Will your account now have $1000 or $1100 or even $900 (incorrect)? What happened to your deposit? The answer in all these cases should be, your deposit is safely completed irrespective of external issues. That’s where the concept of transactions help.

building_a_houseFor another e.g., slightly longer transaction, let’s assume your transaction is building a house. To do this, you go to the bank and get the loan, go to the builder and decide the plan, builder cashes his check upon which his bank transacts with yours, builder goes to construction company and gets the building built and so on. And if any of these individual transactions fail, there are repercussions in others. For .e.g., if your transaction with the bank failed, your transaction with the builder does not happen, even if you previously agreed.

Imagine such transactions – they have to be atomic – a collection of small steps each of which is completed and the teller or the system will take you to the next step. For e.g., when you are at the ATM, if the password step doesn’t complete, you don’t stand a chance of seeing the money (worse case, may even trigger a new transaction with the security guard!). Your transaction with the teller is isolated or independent, we don’t want it to be mixed with your other transactions or someone else’s! And it is durable – once done, the results are set in stone (or computer). We certainly expect the house to be durable at the end of that transaction!! And these concepts are typically applied to the Database, since that’s where your results of your transactions are stored (persisted).

Transactions in a DB
The concept of transactions is not new to Databases. Mainframe databases had them for a long time. Modern Relational Databases (and SQL) are all transactional by nature. See this about.com post for a description of database transactions in general. Every major SQL database vendor support transactions. Some may support Autocommit by default. Some, like Oracle, may leave it to the SQL developer  (you!).

In a relational database, every transaction may contain one or more database operations. In RDBMS these operations are grouped as Data Manipulation Language (DML), Query and Data Declaration Language (DDL). As I mentioned in my previous post, Commits work differently in DMLs and DDLs in Oracle.

The DML statements are the atomic operations that perform a single database operation like INSERT, UPDATE and DELETE. These operations can be performed individually or combined in a group of statements. Together these make a “Transaction”. At the end of such a transaction, you will have to COMMIT or ROLLBACK to make the effect of transaction permanent or ROLLBACK to revert back to before the transaction block, so the database is left unaffected. See here for more on COMMIT etc. Again, COMMIT can be automatic or manual.

I would like to point out one gotcha here: Since DDL automatically commits, you don’t really need to use an explicit COMMIT after a DDL statement (CREATE, ALTER etc). I am somewhat puzzled to see even experienced developers and DBAs sometimes make this mistake. This is true for even CTAS (Create Table As) statements in Oracle.

CREATE TABLE dup_employee AS
SELECT * FROM employee;
-- You don't really need this COMMIT; it's already committed above.
<span style="text-decoration: line-through;">COMMIT;</span>

We say that this is in an implicitly committed transaction. If we expanded the above statement, it may look like this:

COMMIT;
CREATE TABLE dup_employee AS
SELECT * FROM employee;
COMMIT;

Just you or the database don’t have to show it explicitly, as a matter of convenience. But, this definitely

 TRANSACTION Block

When a bunch of SQL statements participate in a single transaction, they are in one single Transactional block. To indicate beginning and end Transaction block, SQL standard talks about transaction control statements. For e.g, a START TRANSACTION (MySQL), BEGIN TRANSACTION (SQL Server) or BEGIN WORK(Informix) marks the beginning of a transaction block. Typically a COMMIT or ROLLBACK would mark the end. To be able commit or rollback to a specific section in the transactional block, databases offer sort of bookmarks, the SAVE POINTS. When you start a transaction block, make sure there is no other transaction block is already open, meaning the last block was committed or rolled back. Otherwise, you will get an error from the database.

In Oracle SQL, you don’t need an explicit “START TRANSACTION” or “BEGIN WORK”  statement to mark the beginning of a transaction. Typically,  the first non-committed executable SQL statement, can be DML or DDL, (implicitly) marks the beginning of a new transaction block. (otherwise, each statement joins the previously open transaction block). A commit or Rollback will end the transaction.
For e.g,
<<implicit beginning of transaction>>

INSERT INTO department (dept_id, dept_name) VALUES (10, 'HR');
INSERT INTO employee (emp_id, employee_name, dept_id) VALUES (100, 'Employee', 10);
INSERT INTO payroll(emp_id, emp_type, pay_rate, pay_rate_type) VALUES(100, 'TEMP', 100, 'HOURLY');

COMMIT;

INSERT INTO lotto_pool (pool_id, emp_id) VALUES (1, 100);

These imaginary inserts are done for each new employee that joins a company. The employee gets added to the employee table and also cross-referenced in a department, payroll tables. Come to think about it, each SQL statement above depends on the previous one to succeed. Thus, they can be part of a single transaction block. If the department does not exist, he cannot be added and if the employee doesn’t exist, he cannot be added to payroll etc.

The above example is just for the illustration of a transaction. Really speaking, the department could have been added long before 100th employee arrives. An employee record could be added well before, his payroll information is setup).

To add little more control to the transaction handling, Databases also provides a marker. In Oracle this is called a SAVEPOINT.

In the above example, if you assume a save point after each DML statement, then you will be able to rollback or commit to a specific save point.

INSERT INTO department (dept_id, dept_name) VALUES (10, 'HR');

SAVEPOINT dept_save;

INSERT INTO employee (emp_id, employee_name, dept_id) VALUES (100, 'Employee', 10);
SAVEPOINT emp_save;
INSERT INTO payroll(emp_id, emp_type, pay_rate, pay_rate_type) VALUES(100, 'TEMP', 100, 'HOURLY');

SAVEPOINT payroll_save;

In this example, we could simply save the department, even if there is an error in employee or payroll.

 

References

http://www.oracle-dba-online.com/sql/commit_rollback_savepoint.htm

https://www-304.ibm.com/support/knowledgecenter/SSGU8G_11.50.0/com.ibm.sqls.doc/ids_sqs_0329.htm

http://dev.mysql.com/doc/refman/5.7/en/commit.html

https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_4010.htm

Oracle Pivot Operator


While working on complex reports, we occasionally have a need to transform Columns to Rows and vice versa. Converting Rows to Columns is typically called Pivot and the opposite, converting Columns to Rows is called Unpivot. Concept of Pivot, and the need for it, has been around for a long time. Excel for example, has a Pivot table to do these type of data transformations. It’s been available in SQL Server for a while. Pivoting is sometimes called Crosstab. PowerBuilder has a datawindow style like that. These are very useful in building data warehouses, reports etc.

Coming back to the topic, Oracle introduced the operators recently (in 11g). Before that you would have to do a round about way. There are several ways to do this, but here is a simple way, when # of columns are small.

CREATE TABLE payment_list(pay_type VARCHAR2(3), description VARCHAR2(30), amount NUMBER);
INSERT INTO payment_list VALUES('CHK', 'Check', 200);
INSERT INTO payment_list VALUES('CRE', 'Credit Card', 500);
INSERT INTO payment_list VALUES('DEB', 'Debit Card', 210);
INSERT INTO payment_list VALUES('ACH', 'ACH payments', 2000);
INSERT INTO payment_list VALUES('CHK', 'Check', 700);

Here is what we get, with a normal SELECT:

DESCRIPTIONAMOUNT
Check
200
Credit Card
500
Debit Card
210
ACH payments
2000
Check
700

PAY_TYPE
CHK CRE DEB ACH CHK

Each row has a payment type and the amount paid. Sometimes, you will want to see these in a column:

SELECT Decode(pay_type, 'CHK', amount, NULL) AS check_pmt,
        Decode(pay_type, 'CRE', amount, NULL) AS cc_pmt,
        Decode(pay_type, 'DEB', amount, NULL) AS debit_pmt,
        Decode(pay_type, 'ACH', amount, NULL) AS ach_pmt
    FROM payment_list;
CHECK_PMT CC_PMT DEBIT_PMT ACH_PMT
200
500
210
2000
700

By using Oracle Decode function, we separated the amount for each type into separate columns. Of course, in reality, we will have lot more rows for each type and we may want to see the Total for each type:

SELECT Decode(pay_type, 'CHK', Sum(amount), NULL) AS check_pmt,
<span style="color: #0000ff;">    Decode(pay_type, 'CRE', Sum(amount), NULL) AS cc_pmt,
Decode(pay_type, 'DEB', Sum(amount), NULL) AS debit_pmt,
Decode(pay_type, 'ACH', Sum(amount), NULL) AS ach_pmt
FROM payment_list
GROUP BY pay_type;</span>
CHECK_PMT CC_PMT DEBIT_PMT ACH_PMT
900
210
2000
500

We can do the same thing with the new Pivot operator:

SELECT * FROM (
	SELECT pay_type, amount
	FROM payment_list)
		PIVOT (Sum(amount)
		FOR pay_type IN ('CHK', 'CRE', 'DEB', 'ACH')
		);
‘CHK’ ‘CRE’ ‘DEB’ ‘ACH’
900 500 210 2000

 

By the way, if you wanted to collapse your DECODE SQL to return single row result like above, you would use:

SELECT Sum(check_pmt) CHK, Sum(cc_pmt) CC, Sum(debit_pmt) DEB, Sum(ach_pmt) ACH FROM 
SELECT Decode(pay_type, 'CHK', Sum(amount), NULL) AS check_pmt,
Decode(pay_type, 'CRE', Sum(amount), NULL) AS cc_pmt,
Decode(pay_type, 'DEB', Sum(amount), NULL) AS debit_pmt,
Decode(pay_type, 'ACH', Sum(amount), NULL) AS ach_pmt
FROM payment_list
GROUP BY pay_type
)

 

Pivot operator is available in many modern databases (and Excel too). I couldn’t find a site that shows Oracle syntax for PIVOT operation in a simple format yet. However, Oracle’s site has great content on the operator (and unpivot) here.  For sake of completeness, I am including the SQL syntax Pivot operator in SQL Server. The only difference, I could see here is SQL Server’s usage of square brackets instead of quotes.

SELECT &lt;non-pivoted column&gt;,
	[ pivoted column] AS &lt;column name&gt;,
	[ pivoted column] AS &lt;column name&gt;,
	...
	[ pivoted column] AS &lt;column name&gt;
FROM(&lt;SELECT query that produces the data&gt;) AS &lt;alias for the source query&gt;
PIVOT
(
    &lt;aggregation function&gt;(column)
    FOR
    [&lt;column that contains the values that will become column headers&gt;]
    IN ( [pivoted column], [pivoted column],
    ... [pivoted column])
AS &lt;alias for the pivot table&gt;
[ORDER BY clause];

That’s all folks. I know, it has a weird syntax, but The inner most SQL (SELECT query that produces the data), is what you are pivoting. The row(s) for each value in the FOR .. IN list will be converted to a column. So, when the inner SQL finishes, you will get the columns CHK, CRE, DEB, ACH. SELECT * in the outer SQL, makes sure all these columns are picked up.

To be contd…

Gotcha: “BEGIN” in Oracle


If you are coming to Oracle from another database, you are in for some surprises. One of them is the key word BEGIN. They don’t mean the same in Oracle. Read on.

A TRANSACTION block

In SQL Standards they list Transaction control statements such as START TRANSACTION, END TRANSACTION and SAVE TRANSACTION. Different database vendors implement this as they see fit. For e.g., Informix always had a “BEGIN [WORK]” statement to mark the beginning of a transaction block. The block will end when an explicit COMMIT or ROLLBACK is issued or SQL ends abnormally (in which case it’s rolled back). See here.

So in non-ORACLE database (Informix for e.g) you may have,

BEGIN [WORK]
--(1)
INSERT....
UPDATE...
DELETE...
..
COMMIT [WORK];

-- **commit marks the end of the current transaction**

In Oracle there is no BEGIN [WORK] equivalent. It’s just “understood” when the first executable statement makes some changes to the database. This includes DML and DDL statements. So, the above statement block in Oracle will be written as,

-- **implicit Transaction block begins?**
--(1)
INSERT....
UPDATE...
DELETE...
..
COMMIT [WORK];

-- **commit marks the end of the current transaction**

So, there you have it. In Oracle, an implicit transaction block is started when the first INSERT statement is executed and it is not started with a BEGIN or START.

BEGIN…END in Oracle

But then, you have surely seen a BEGIN (and END) in Oracle? Yes, you are correct about that statement being there, only it’s a statement in PL/SQL not plain SQL. PL/SQL is a separate procedural programming language in Oracle, just like C or Java, only PL/SQL is specialized to handle SQL more efficiently1.

This BEGIN statement in PL/SQL can be easily confused with BEGIN [WORK] in other databases, if you are new to Oracle. A BEGIN..END block in PL/SQL is merely a syntactical collection of statements (SQL, non-SQL). They don’t relate to transactions. However, since a bunch of SQL statements inside the BEGIN..END block mark a single PL/SQL statement, a COMMIT (or ROLLBACK) after a PL/SQL block could commit (or rollback) entire list of SQL statements within the PL/SQL block, thus implying BEGIN had to do with a new transaction. Not so! Suppose you ran the below block in SQL*Plus. Let’s say you updated a table. Try SELECTing the record you just updated, in another session. You won’t see the change yet, because we have not committed the below statement. That also shows END doesn’t have anything to do with Transaction.

-- **PLSQL BLock 1**
BEGIN
-- **SQL statements**
END;
/

And as another example, if you had couple of PL/SQL block (BEGIN...END) before a COMMIT is issued, statements in both those blocks are committed.

-- **PLSQL BLock 1**
BEGIN
--**SQL statements**
END;
/

-- **PLSQL BLock 2**
BEGIN
--**SQL Statements**
END;
/

COMMIT;

Here COMMIT applies to all the statements PLSQL blocks 1 and 2, showing us that PL/SQL block does not correspond to a transaction block.

[1] I think, differentiating between SQL and PL/SQL is the biggest challenge a newcomer to Oracle faces. PL/SQL is a procedural language with control structures like if, loops etc. You can build stored programs like Oracle Procedures, Triggers, Functions and Packages only in PL/SQL. It’s tightly coupled with Oracle SQL Engine causing some confusion. I will post more on this later.

Oracle SQL*PLUS: How to silence SQL*Plus


While researching about Oracle Command Line parameters, stumbled on the following question.
https://forums.oracle.com/message/2449066

The OP is asking how to silence SQL*Plus, so he/she could turn off all the prompts coming out in the log. The simple answer is, using command line option -S, like this:

 

sqlplus -s user/password@db @script

Of course, there are more commadline options available. See here for the details.

I notice a couple of things in this post. The question is simple and the answer is not so intuitive. SQL*Plus has so many SETs for everything else. Why not SET silent ON or something? Instead, they have a command line option -S to silence the tool. Even HTML mode is allowed both ways -M or SET MARKUP! Talk about being consistent (or not!).

SET ECHO OFF seems to be relevant, but it has a different meaning. It only turns off echoing the SQLs. The output still comes out with SQL prompt. The effect of this is more visible, if you run SQL*Plus in batch mode.

There is another setting called FEEDBACK. This also sounds logical for silencing SQL*Plus. This one actually only turns off the feedback in the result, like “1 row selected”.

So, there is no way to completely silence SQL*Plus using only SETs.

Coming back to the post again, I find a few things worth mentioning here:

See the script has a “/” at the end. This will make the SQL(s) above to run again. In fact, Sybrand Bakker mentions this in his answers, that was completely ignored. OP seems knowledgeable, so it could be a slip. I hope the OP noted and corrected it. Though, it doesn’t do any harm here (imagine this was a DML or a DDL, then the effect may be more profound), the SELECT will be executed twice. What if it was a long running query? Do you see the resources being wasted in the duplicate run?

The other thing that bothered me with this is the suggestion to turn off SQLPROMPT, thus:

set sqlprompt ' '

Though it seems logical, it only removes the prompt “SQL>”!! I hope this poster is not using such to somehow “clean” the output. It definitely won’t leave out the banner etc. I hope the poster did not write a script to remove those. The reason I am pointing this out, I’ve seen such scripts at work before!! Try to use the language/tool feature as much as possible.

QuickTip: Oracle SQLPlus – (IF) Check for parameters to script


I was making a generic SQL script. To make it generic, I decided to use Command line arguments to the script.

To do this, I created a defined variable that is set to the 1st parameter (&1). Then, throughout the script, I can use &ACCT_NBR, instead of hard coding the value.

DEFINE ACCT_NBR='&1'
SELECT * from account WHERE acct_nbr = &ACCT_NBR.

This worked fine. You just have to SET DEFINE ON before using & in your script.

(Of course, the actual script has multiple SQLs using this variable. I just included a simple one above to show the usage.)

But then, I was worried about future users of the script forgot to add command line argument. The whole script may go crazy. I wanted to have sort of an IF check and exit before any other script was executed, in case the parameter was missing. But, plain SQL does not offer this in ORACLE. (PL/SQL does, but that’s a whole different language with it’s own quirks! For my task, PL/SQL might have been an overkill).

After trying several things, I came up with the below lines:

WHENEVER SQLERROR EXIT SQL.SQLCODE

DEFINE ACCT_NBR='&1'
SELECT &ACCT_NBR FROM dual;

This prompts for the value of (parameter) 1, if the parameter was missing from command line.

12:07:13 SQL> DEFINE ACCT_NBR='&1'
Enter value for 1:

Here I pressed enter, thus the parameter is not set. It errors out with the below message:

12:07:13 SQL> SELECT &ACCT_NBR FROM dual;
old 1: SELECT &ACCT_NBR FROM dual
new 1: SELECT FROM dual
SELECT FROM dual
*

 

ERROR at line 1:
ORA-00936: missing expression</pre>
<strong></strong>

and the script exits.

So, there you have it. an IF check without the IF in SQL!

Oracle SQL*Plus – Forward slash contd… (PL/SQL)


My friend was running an old PL/SQL script. The original developer seemed to have used another tool to run it, which was more pardoning with syntax than SQL*Plus. This exposed some new behaviors around (or lack of) Forward slash.

The scripts were spooling into log files. For one of the scripts, he kept getting numbers!! Like,

201
202
203

It was puzzling, because we were not printing anything in the PL/SQL block except errors. After analyzing the code for a bit, realized the script was missing the (REQUIRED) Forward slash after the PL/SQL block!!!

Here is a simple example:

test1.sql

declare
i number;

begin
select 1 into i from dual;
end;
<EOF>

I just included <EOF> here to show that’s the end of file.

Run the above script on command line as,

sqlplus <login> @test.sql

Now, you will see number 9 printed. If you keep pressing enter now, number will start incrementing. Unless you know what’s going on here, you could just fill up the screen with numbers quickly, like so:

9
10
11
12
13

At this point, if you know the problem, you will just press a “/” and off it will go. Or you can do a CTRL-C to kill the script!!

The above behavior is because the script file was missing “/” at the end of PL/SQL block! So, next time you code your PL/SQL block, make sure to include that blessed “/” at the end of the block!

Oracle SQL*Plus – Forward Slash contd…


Recently while working with a coworker I saw trouble with using Forward slash, first hand. He was running some old scripts. The original developer probably ran these in some other tool (typically other tools are more pardoning than SQL*Plus.

One of the scripts had DDLs and had some code commented out. Incidentally, the original developer used C-Style comments (those in between /* … */). And inadvertently, he created a Forward slash problem!!!!

I am including a very simple script below to illustrate this:

create table t (c VARCHAR2(30));
/* drop table t;
*/

First line there is a DDL that creates a new table. There is the C-Style comment.

SQL> create table t (c VARCHAR2(30));
/* drop table t;
*/

create table t (c VARCHAR2(30))
*
ERROR at line 1:
ORA-00955: name is already used by an existing object

No way!!!! How come?? Well, you guessed it! It’s because of a Forward slash the developer left in the script inadvertently. Remember C-Style comment starts with Forward slash?? Forward slash means "RUN" to SQL*Plus. It tried to execute the previous DDL when it saw /* on the second line, thus the Error!

Funnily, this doesn’t happen in a DML nor in a PL/SQL block. So, if your scripts have DDLs beware of Forward slashes in any shape or form!!!

Quick Tip: Oracle Sqlplus – changing prompt


Oracle SQL*Plus has several settings that can be customized using SET command. When you run SQL*Plus, typically the following (boring?) prompt greets you.

SQL>

If you ever want to change this to something more interesting, there is a SET option for that.

SET sqlprompt ‘sqlplus:&_user@&_connect_identifier > ‘

This will be shown as,

sqlplus:<user>@<SID>>

For e.g.,

scott@hr

Where scott is a user id logged into hr database.

References

http://docs.oracle.com/cd/B28359_01/server.111/b31189/ch12040.htm
http://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12017.htm#i2675128

Oracle: Commit and Forward Slash


Further to my post earlier about DDL and Forward Slash, I would like to explain Forward slash’s role (or lack thereof) in other scenarios. One such scenario people seem to get confused with is COMMIT. They seem to want to find some type of connection between the two. Let me say it upfront, there is no connection between COMMIT and “/”.

Forward Slash is simply a SQL Executor and it happens to show up in various situations. This combined with SQL*Plus’s own quirks seem to imply certain other functions for this character. Let me repeat: it’s just an executor – short for RUN command in SQL*Plus.

One reason it could be construed as connected with COMMIT is that it may often be the last character in a file. When you run SQL*Plus in batch mode (running a script in a file), it typically exits after the last line (on *nix. It doesn’t DoS/Windows). And the SQL*Plus’s own behavior is to COMMIT a transaction when exiting. (Why? See here for a nice discussion about this.) So, it’s the exit that committed not the “/”. But. you can see why some newcomer to Oracle who just inherited some scripts, may think that “/” (being the last statement) actually committed the transaction!!!

The other reason could be DDL. DDLs are implicitly committed. (Actually it commits before and after the DDL statement itself, so beware if you are mixing DMLs and DDLs).

Now, typically a DDL could be ended with “/”. For e.g.,

INSERT INTO department(100, 'HR');

CREATE TABLE employee(
    emp_id NUMBER,
    emp_name VARCHAR2(40)
/

In the above case, department  table will have the entry, even if the CREATE TABLE below failed. In generla, any DML (like INSERT/UPDATE/DELETE) before a DDL, DML would have been committed, even if the DDL itself failed. Since “/” is the last statement here, one could think, “/” did it!!! Another strike against “/”.

So, trust me folks! “/” doesn’t do anything else, except sending a SQL to Oracle in SQL*Plus. It’s just a shortcut for Run Command. But, depending on the situation, it may look like it’s doing something more. In such cases, analyze the SQL and try to rearrange them. Try to keep DMLs separate from DDLs to avoid surprises. And of course, set some standards for punctuations and stick to it to avoid any undesired effects.

Oracle: DDL and Forward Slash


In this blog,  I write about various technologies I come across. Only a few topics seem to get a lot of attention. One of them happens to be Forward slash in (Oracle) SQL. I’ve already posted about this here and here. When I look at the search terms that lead to my site, I sense a lack of understanding of it’s usage. A search through the web quickly reveals that forward slash has been a source of confusion even among some of the experienced Oracle SQL developers 1. Based on the search terms, I can see the newcomers are often getting confused by the SQLs left for them by their predecessors and are looking for a clear explanation. “/” is often mixed in various statements, such as DDL, COMMIT etc and this leads to confusion about the statement itself. I will try to explain the role of “/” (or lack thereof) in each different scenario in separate posts.

One of the search term that I get a lot is “DDL and Forward slash” or some variation of it. Forward slash has nothing to do with DDL per se. If you saw my other posts, it is just a statement executor (RUN command for both SQL and PL/SQL). Unfortunately, semi-colon doubles as statement terminator and executor for SQLs (poor choice of shortcut) in SQL*Plus. There lies the confusion. Thus, the CREATE TABLE or DROP VIEW statements can be terminated by a semi-colon as well as “/”.

For e.g.,

CREATE TABLE employee(
    emp_id NUMBER,
    emp_name VARCHAR2(40));

and

CREATE TABLE employee(
    emp_id NUMBER,
    emp_name VARCHAR2(40))
/

are identical as for SQL*Plus is concerned. But, as a matter of preference, people tend to use “/” in DDL.

But this is not the case in other DDL statements that involve PL/SQL:

CREATE OR REPLACE FUNCTION f_get_employee(a_emp_id NUMBER)
RETURN VARCHAR2
AS
    v_emp_name VARCHAR2(40);
BEGIN
   SELECT emp_name
   INTO v_emp_name
   FROM employee
   WHERE emp_id = a_emp_id;
END;
/

Here “/” is a must, as the above statement is one PL/SQL statement (from CREATE to END;). Remember, PL/SQL block ends with END; and it has to be followed by “/” to be executed 2 (see notes below).  Thus some DDL statements (those that create programming constructs like packages, procedures, triggers) require “/” at the end. This, I think, led the newcomers to believe there is a link between “/” and DDL!!! There is no connection! You cannot generalize the use of “/” for all DDLs. For e.g., if you tried to do this (semi-colon followed by “/”) in a CREATE TABLE statement, you will have unexpected behavior:

SQL> CREATE TABLE employee(
2 emp_id NUMBER,
3 emp_name VARCHAR2(40));
Table created.SQL> /
CREATE TABLE employee(
*
ERROR at line 1:
ORA-00955: name is already used by an existing objectSQL>

In this case, the semi-colon followed by slash is a bad idea.

In a nutshell, use only semi-colon or slash at the end of a SQL (DML or DDL) and semi-colon followed by slash at the end of a PL/SQL. See here for the difference between SQL and PL/SQL).

As a personal choice, I normally use “/” for all DDLs (SQL or PL/SQL) and semi-colon for all DMLs. You can have one DDL per file in which case, the “/” is the last character in the file. You can also have multiple DDLs in a file, each separated by “/”.  Try not to mix DDLs and DMLs in the same file! This may lead to more confusion and errors!

References

http://www.orafaq.com/faq/what_are_the_difference_between_ddl_dml_and_dcl_commands

http://docs.oracle.com/html/A90108_01/sqcmd.htm

Notes:


1. See here for e.g., https://forums.oracle.com/thread/1020117

2. When I say “executed”, I mean the PL/SQL block that creates the function/procedure is executed – meaning the program is compiled and stored in the database. (Hence the name stored programs.) The actual program execution is done by

SELECT <function name> FROM dual;

OR EXEC <procedure name>