12

I have an Oracle database, and a table with several not null columns, all with default values.

I would like to use one insert statement for any data I want to insert, and don't bother to check if the values inserted are nulls or not.

Is there any way to fall back to default column value when null is inserted?

I have this code:

<?php
if (!empty($values['not_null_column_with_default_value'])) {
    $insert = "
        INSERT INTO schema.my_table
            ( pk_column, other_column, not_null_column_with_default_value)
        VALUES
            (:pk_column,:other_column,:not_null_column_with_default_value)
     ";
} else {
    $insert = "
        INSERT INTO schema.my_table
            ( pk_column, other_column)
        VALUES
            (:pk_column,:other_column)
     ";        
}

So, I have to omit the column entirely, or I will have the error "trying insert null to not null column". Of course I have multiple nullable columns, so the code create insert statement is very unreadable, ugly, and I just don't like it that way.

I would like to have one statement, something similar to:

INSERT INTO schema.my_table
    ( pk_column, other_column, not_null_column_with_default_value)
VALUES
    (:pk_column,:other_column, NVL(:not_null_column_with_default_value, DEFAULT) );

That of course is a hypothetical query. Do you know any way I would achieve that goal with Oracle DBMS?

EDIT:

Thank you all for your answers. It seams that there is no "standard" way to achieve what I wanted to, so I accepted the IMO best answer: That I should stop being to smart and stick to just omitting the null values via automatically built statements.

Not exactly what I would like to see, but no better choice.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
SWilk
  • 3,261
  • 8
  • 30
  • 51
  • why don't you enter default value of the column "not_null_column_with_default_value" instead of the word "DEFAULT" inside the "hypothetical query"? – palindrom Mar 22 '11 at 09:06
  • Cause I do not know what is the default value at the moment of inserting. But as @Vincent Malgrat suggested I can fetch that form ALL_TAB_COLUMNS – SWilk Mar 22 '11 at 09:54

5 Answers5

16

For those who reading it now:

In Oracle 12c there is new feature: DEFAULT ON NULL. For example:

CREATE TABLE tab1 (
  col1        NUMBER DEFAULT 5,
  col2        NUMBER DEFAULT ON NULL 7,
  description VARCHAR2(30)
);

So when you try to INSERT null in col2, this will automatically be 7.

Nick
  • 882
  • 2
  • 9
  • 31
Lisa Aaron
  • 186
  • 1
  • 6
3

As explained in this AskTom thread, the DEFAULT keyword will only work as a stand-alone expression in a column insert and won't work when mixed with functions or expressions such as NVL.

In other words this is a valid query:

INSERT INTO schema.my_table
    ( pk_column, other_column, not_null_column_with_default_value)
VALUES
    (:pk_column,:other_column, DEFAULT)

You could use a dynamic query with all rows and either a bind variable or the constant DEFAULT if the variable is null. This could be as simple as replacing the string :not_null_column_with_default_value with the string DEFAULT in your $insert.

You could also query the view ALL_TAB_COLUMNS and use nvl(:your_variable, :column_default). The default value is the column DATA_DEFAULT.

Vincent Malgrat
  • 66,725
  • 9
  • 119
  • 171
  • Thank you for suggestion about data_default. I think I'll create a function `get_default(:schema,:table,:column)` which will utilize oracle cache, so I should not be very slow. I will only have to check if the type can be converted to other types without problems. – SWilk Mar 22 '11 at 09:52
  • 1
    @SWilk: The type is the old CLOB. It is just text and should be converted to a string. In your query you should *replace* DEFAULT by the value of this column, and not *bind* the value because the default value can be an expression (such as `trunc(sysdate)+1`) – Vincent Malgrat Mar 22 '11 at 10:29
  • 2
    Querying the data dictionary for the default value on each and every insert will definitely ruin your performance. I'd use it only if insert performance is not an issue for your application. – Martin Schapendonk Mar 22 '11 at 12:49
1

I think the cleanest way is to not mention them in your INSERT-statement. You could start writing triggers to fill default values but that's heavy armor for what you're aiming at.

Isn't it possible to restructure your application code a bit? In PHP, you could construct a clean INSERT-statement without messy if's, e.g. like this:

<?php
$insert['column_name1'] = 'column_value1';
$insert['column_name2'] = 'column_value2';
$insert['column_name3'] = '';
$insert['column_name4'] = 'column_value4';

// remove null values
foreach ($insert as $key => $value) {
  if (is_null($value) || $value=="") {
    unset($insert[$key]);
  }
}

// construct insert statement
$statement = "insert into table (". implode(array_keys($insert), ',') .") values (:". implode(array_keys($insert), ',:') .")";

// call oci_parse
$stid = oci_parse($conn, $statement);

// bind parameters
foreach ($insert as $key => $value) {
  oci_bind_by_name($stid, ":".$key, $value);
}

// execute!
oci_execute($stid);
?>
Martin Schapendonk
  • 12,893
  • 3
  • 19
  • 24
  • And then "O'Neil" comes as the value and BUM! That would actually work if converted to bound variables (:values). Anyway, I am aware of this pattern, but I was looking for any kind of oracle statement that would do the job. I thought oracle might have a ready solution for this. – SWilk Mar 22 '11 at 09:36
  • @SWilk you're right. My example can easily be modified to use bind variables. In fact, I just did. – Martin Schapendonk Mar 22 '11 at 12:41
1

The better option for performance is the first one.

Anyway, as I understand, you don't want to repeat the insert column names and values due the difficult to make modifications. Another option you can use is to run an insert with returning clause followed by an update:

INSERT INTO schema.my_table
    ( pk_column, other_column, not_null_column_with_default_value)
VALUES
    (:pk_column,:other_column, :not_null_column_with_default_value)
RETURNING not_null_column_with_default_value 
INTO :insered_value

It seems to work with PHP.

After this you can check for null on insered_value bind variable. If it's null you can run the following update:

UPDATE my_table
   SET not_null_column_with_default_value  = DEFAULT
 WHERE  pk_column = :pk_column: 
FerranB
  • 35,683
  • 18
  • 66
  • 85
0

I would like to use one insert statement for any data I want to insert, and don't bother to check if the values inserted are nulls or not.

Define your table with a default value for that column. For example:

create table myTable
(
  created_date date default sysdate,
  ...
)
tablespace...

or alter an existing table:

alter table myTable modify(created_date default sysdate);

Now you (or anyone using myTable) don't have to worry about default values, as it should be. Just know that for this example, the column is still nullable, so someone could explicitly insert a null. If this isn't desired, make the column not null as well.

EDIT: Assuming the above is already done, you can use the DEFAULT keyword in your insert statement. I would avoid triggers for this.

tbone
  • 15,107
  • 3
  • 33
  • 40
  • The columns have default values. The thing is I want to insert the default value when the bound variable is null. `insert into tmp_table_tab (aaa,bbb,ccc) values(9, 8, nvl(:my_value,DEFAULT))` results in ORA-00936 missing expression. While `insert into tmp_table_tab (aaa,bbb,ccc) values(9, 8, DEFAULT)` would execute correctly, it does something else than I need. I would like to forget about the value of :my_Value and use one INSERT statement for all inserts. – SWilk Mar 22 '11 at 11:05
  • ok, i understand now. Unfortunately, you're probably not going to find an eloquent solution to have one master insert for all columns null or not. If you can't change your insert stmt based on which cols are nulls, then a before insert trigger would be the next best thing. Keep in mind that if a column is changed from not null to nullable, then you MUST also change the trigger to exclude using default values for that column. (adds additional maintenance overhead as well as processing overhead). – tbone Mar 22 '11 at 11:46