msgbartop
News, views, tips and tricks on Oracle and other fun stuff
msgbarbottom

The Lazy Developer’s Way to Populate a Surrogate Key

Consider this table:

CREATE TABLE t
  (
     id   NUMBER PRIMARY KEY,
     name VARCHAR2(100)
  );

You want the column id to be populated from this sequence:

CREATE SEQUENCE t_s;

Here are a couple of ways you can do this:

1- Use the sequence’s next value directly in the insert statement:

INSERT INTO t (id, name) VALUES (t_s.nextval,'Eddie');

2- Use a database trigger:

CREATE OR REPLACE TRIGGER t_trig
   BEFORE INSERT
   ON t
   FOR EACH ROW
BEGIN
   SELECT t_s.NEXTVAL
     INTO :NEW.ID
     FROM DUAL;
END;

You will then insert a new row like this:

INSERT INTO t (name) VALUES ('John');

And you do not have to worry about the id column, unless of course you need it in subsequent code. In this case, use the RETURNING clause:

DECLARE
   l_id t.id%TYPE;
BEGIN
   INSERT INTO t (NAME) VALUES ('John')
     RETURNING id
          INTO l_id;
   DBMS_OUTPUT.put_line ('id: ' || l_id);
END;

So, when populating a surrogate key, which is better: using the sequence.nextval directly in the insert statement or using a database trigger?

In general, I do not like database triggers, they hide your logic and make it difficult to debug. But, in this case, I believe that using a database trigger to populate a primary key with a sequence value – or any unique value for that matter – is the right thing to do. Why? Well, I was troubleshooting an ORA-00001: unique constraint violated on a table’s primary key. The PL/SQL API that inserts rows into this table populates the key from a sequence, using the sequence.nextval in the insert statement.

A developer decides to “manually” insert rows into this table, bypassing the API all together. So, he queries the last number from the table’s primary key, and then inserts rows with hardcoded numbers in the key. He adds 1 to the number each time he inserts a new row. He does not use the sequence.nextval. Guess what happens next!

The next time the API is called to create a new row, the sequence generates its next number, but oops! this is the same number that the developer has already used. Boom! ORA-00001: unique constraint violated.

If the primary key was populated from a trigger, bypassing the value given to it from the developer, this error would not have happened.

Filed in Oracle with 7 Comments | Tags:


When The Combination of an Oracle Sequence and a PLS_INTEGER is Deadly

The following is a true story. We had our Oracle MRP system down for a couple of days because of a simple, yet hidden bug that went unnoticed for years until it hit us couple of days ago (somehow the image of a terrorist cell comes to mind).

In Oracle Applications (11.03) there is a table that stores forecast records (mrp_forecast_dates). In this table there is the column transaction_id that gets populated from a sequence (mrp_forecast_dates_s).

When the current value of the sequence exceeded the number 2,147,483,647 the MRP concurrent programs started to fail with an “ORA-01426: numeric overflow” error. After a lot of research, and an Oracle Service Request, we discovered what the problem was and what was special about the number 2,147,483,647.

Let me reproduce the problem in the following example: (more…)

Filed in Oracle, Tips with 3 Comments | Tags: , ,


CURRVAL and NEXTVAL

Here are some facts you may want to consider if you use or are planning to use sequences in Oracle.

You cannot use CURRVAL and NEXTVAL in the following constructs:

  • A subquery in a DELETE, SELECT, or UPDATE statement
  • A query of a view or of a materialized view
  • A SELECT statement with the DISTINCT operator
  • A SELECT statement with a GROUP BY clause or ORDER BY clause
  • A SELECT statement that is combined with another SELECT statement with the UNION, INTERSECT, or MINUS set operator
  • The WHERE clause of a SELECT statement
  • DEFAULT value of a column in a CREATE TABLE or ALTER TABLE statement
  • The condition of a CHECK constraint

Also, within a single SQL statement that uses CURRVAL or NEXTVAL, all referenced LONG columns, updated tables, and locked tables must be located on the same database.

When you create a sequence, you can define its initial value and the increment between its values. The first reference to NEXTVAL returns the sequence’s initial value. Subsequent references to NEXTVAL increment the sequence value by the defined increment and return the new value. Any reference to CURRVAL always returns the sequence’s current value, which is the value returned by the last reference to NEXTVAL. Note that before you use CURRVAL for a sequence in your session, you must first initialize the sequence with NEXTVAL.

Filed in Oracle with Comments Off | Tags: ,