51

I have a table that has a column with a default value:

create table t (
    value varchar(50) default ('something')
)

I'm using a stored procedure to insert values into this table:

create procedure t_insert (
    @value varchar(50) = null
)
as 
insert into t (value) values (@value)

The question is, how do I get it to use the default when @value is null? I tried:

insert into t (value) values ( isnull(@value, default) )

That obviously didn't work. Also tried a case statement, but that didn't fair well either. Any other suggestions? Am I going about this the wrong way?

Update: I'm trying to accomplish this without having to:

  1. maintain the default value in multiple places, and
  2. use multiple insert statements.

If this isn't possible, well I guess I'll just have to live with it. It just seems that something this should be attainable.

Note: my actual table has more than one column. I was just quickly writing an example.

2
  • Just curious, because you posed a good question, but why can't you change the table definition to be not null? Commented Oct 24, 2008 at 17:13
  • 9
    I love the ISNULL(@value, DEFAULT) syntax - sure wish that worked. Commented Jul 26, 2012 at 14:00

16 Answers 16

20

Christophe,

The default value on a column is only applied if you don't specify the column in the INSERT statement.

Since you're explicitiy listing the column in your insert statement, and explicity setting it to NULL, that's overriding the default value for that column

What you need to do is "if a null is passed into your sproc then don't attempt to insert for that column".

This is a quick and nasty example of how to do that with some dynamic sql.

Create a table with some columns with default values...

CREATE TABLE myTable (
    always VARCHAR(50),
    value1 VARCHAR(50) DEFAULT ('defaultcol1'),
    value2 VARCHAR(50) DEFAULT ('defaultcol2'),
    value3 VARCHAR(50) DEFAULT ('defaultcol3')
)

Create a SPROC that dynamically builds and executes your insert statement based on input params

ALTER PROCEDURE t_insert (
    @always VARCHAR(50),
    @value1 VARCHAR(50) = NULL,
    @value2 VARCHAR(50) = NULL,
    @value3 VARCAHR(50) = NULL
)
AS 
BEGIN
DECLARE @insertpart VARCHAR(500)
DECLARE @valuepart VARCHAR(500)

SET @insertpart = 'INSERT INTO myTable ('
SET @valuepart = 'VALUES ('

    IF @value1 IS NOT NULL
    BEGIN
        SET @insertpart = @insertpart + 'value1,'
        SET @valuepart = @valuepart + '''' + @value1 + ''', '
    END

    IF @value2 IS NOT NULL
    BEGIN
        SET @insertpart = @insertpart + 'value2,'
        SET @valuepart = @valuepart + '''' + @value2 + ''', '
    END

    IF @value3 IS NOT NULL
    BEGIN
        SET @insertpart = @insertpart + 'value3,'
        SET @valuepart = @valuepart + '''' + @value3 + ''', '
    END

    SET @insertpart = @insertpart + 'always) '
    SET @valuepart = @valuepart + + '''' + @always + ''')'

--print @insertpart + @valuepart
EXEC (@insertpart + @valuepart)
END

The following 2 commands should give you an example of what you want as your outputs...

EXEC t_insert 'alwaysvalue'
SELECT * FROM  myTable

EXEC t_insert 'alwaysvalue', 'val1'
SELECT * FROM  myTable

EXEC t_insert 'alwaysvalue', 'val1', 'val2', 'val3'
SELECT * FROM  myTable

I know this is a very convoluted way of doing what you need to do. You could probably equally select the default value from the InformationSchema for the relevant columns but to be honest, I might consider just adding the default value to param at the top of the procedure

Sign up to request clarification or add additional context in comments.

1 Comment

May you provide an example of “You could probably equally select the default value from the InformationSchema for the relevant columns”?
11

Try an if statement ...

if @value is null 
    insert into t (value) values (default)
else
    insert into t (value) values (@value)

5 Comments

...I clicked up vote too soon! Doah! Well, I won't give you a down-vote but it's not quite the right answer! The default value is used when there's NO value provided for the attribute.
Using the DEFAULT keyword indicates that you wish the database to use the default value for that field. See msdn.microsoft.com/en-us/library/ms174335(SQL.90).aspx. When I tested this, it did insert the default value for the column when no parameter was provided to the proc.
This is probably the best way to do it, unfortunately you cannot combine COALESCE and DEFAULT to remove the IF.
What happens when you have a series of parameters than may need to fall back on defaults?
OP asks for “without having to use multiple insert statements.”
4

As far as I know, the default value is only inserted when you don't specify a value in the insert statement. So, for example, you'd need to do something like the following in a table with three fields (value2 being defaulted)

INSERT INTO t (value1, value3) VALUES ('value1', 'value3')

And then value2 would be defaulted. Maybe someone will chime in on how to accomplish this for a table with a single field.

Comments

4

The best option by far is to create an INSTEAD OF INSERT trigger for your table, removing the default values from your table, and moving them into the trigger.

This will look like the following:

create trigger dbo.OnInsertIntoT
ON TablenameT
INSTEAD OF INSERT
AS
insert into TablenameT
select
   IsNull(column1 ,<default_value>)
  ,IsNull(column2 ,<default_value>)
  ...
from inserted

This makes it work NO MATTER what code tries to insert NULLs into your table, avoids stored procedures, is completely transparent, and you only need to maintain your default values in one place, namely this trigger.

Comments

3

This is the best I can come up with. It prevents sql injection uses only one insert statement and can ge extended with more case statements.

CREATE PROCEDURE t_insert (     @value varchar(50) = null )
as
DECLARE @sQuery NVARCHAR (MAX);
SET @sQuery = N'
insert into __t (value) values ( '+
CASE WHEN @value IS NULL THEN ' default ' ELSE ' @value ' END +' );';

EXEC sp_executesql 
@stmt = @sQuery, 
@params = N'@value varchar(50)',
@value = @value;

GO

Comments

3

Probably not the most performance friendly way, but you could create a scalar function that pulls from the information schema with the table and column name, and then call that using the isnull logic you tried earlier:

    CREATE FUNCTION GetDefaultValue
    (
        @TableName VARCHAR(200),
        @ColumnName VARCHAR(200)
    )
    RETURNS VARCHAR(200)
    AS
    BEGIN
        -- you'd probably want to have different functions for different data types if
        -- you go this route
    RETURN (SELECT TOP 1 REPLACE(REPLACE(REPLACE(COLUMN_DEFAULT, '(', ''), ')', ''), '''', '') 
            FROM information_schema.columns
            WHERE table_name = @TableName AND column_name = @ColumnName)

    END
    GO

And then call it like this:

INSERT INTO t (value) VALUES ( ISNULL(@value, SELECT dbo.GetDefaultValue('t', 'value') )

Comments

3

chrisofspades,

As far as I know that behavior is not compatible with the way the db engine works, but there is a simple (i don't know if elegant, but performant) solution to achive your two objectives of DO NOT

  1. maintain the default value in multiple places, and
  2. use multiple insert statements.

The solution is to use two fields, one nullable for insert, and other one calculated to selections:

CREATE TABLE t (
    insValue VARCHAR(50) NULL
    , selValue AS ISNULL(insValue, 'something')
)

DECLARE @d VARCHAR(10)
INSERT INTO t (insValue) VALUES (@d) -- null
SELECT selValue FROM t

This method even let You centralize the management of business defaults in a parameter table, placing an ad hoc function to do this, vg changing:

selValue AS ISNULL(insValue, 'something')

for

selValue AS ISNULL(insValue, **getDef(t,1)**)

I hope this helps.

Comments

1

You can use default values for the parameters of stored procedures:

CREATE PROCEDURE MyTestProcedure ( @MyParam1 INT,
@MyParam2 VARCHAR(20) = ‘ABC’,
@MyParam3 INT = NULL)
AS
BEGIN
    -- Procedure body here

END

If @MyParam2 is not supplied, it will have the 'ABC' value...

2 Comments

This requires that you maintain the default values in at least two places though
Precisely Tom, and I'd like to avoid doing that.
0

You can use the COALESCE function in MS SQL.

INSERT INTO t ( value ) VALUES( COALESCE(@value, 'something') )

Personally, I'm not crazy about this solution as it is a maintenance nightmare if you want to change the default value.

My preference would be Mitchel Sellers proposal, but that doesn't work in MS SQL. Can't speak to other SQL dbms.

1 Comment

I think the OP’s point is that INSERT INTO t(value) VALUES(COALESCE(@value, DEFAULT)) yields a syntax error.
0

Don't specify the column or value when inserting and the DEFAULT constaint's value will be substituted for the missing value.

I don't know how this would work in a single column table. I mean: it would, but it wouldn't be very useful.

2 Comments

That would require writing an IF statement to determine if @value is null, which would lead to having two INSERT statements, which I'm hoping is avoidable. Note: my actual table has more than one column. I was just quickly writing an example.
In PL/SQL you could easily solve this problem with your column's data types, but in T-SQL you're programming like its 1989.
0

Hope To help to -newbie as i am- Ones who uses Upsert statements in MSSQL.. (This code i used in my project on MSSQL 2008 R2 and works simply perfect..May be It's not Best Practise.. Execution time statistics shows execution time as 15 milliSeconds with insert statement)

Just set your column's "Default value or binding" field as what you decide to use as default value for your column and Also set the column as Not accept null values from design menu and create this stored Proc..

`USE [YourTable]
GO


SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[YourTableName]

    @Value smallint,
    @Value1 bigint,
    @Value2 varchar(50),
    @Value3 varchar(20),
    @Value4 varchar(20),
    @Value5 date,
    @Value6 varchar(50),
    @Value7 tinyint,
    @Value8 tinyint,
    @Value9 varchar(20),
    @Value10 varchar(20),
    @Value11 varchar(250),
    @Value12 tinyint,
    @Value13 varbinary(max) 

-- in my project @Value13 is a photo column which storing as byte array.. --And i planned to use a default photo when there is no photo passed --to sp to store in db..

AS
--SET NOCOUNT ON
IF @Value = 0 BEGIN
    INSERT INTO YourTableName (
        [TableColumn1],
        [TableColumn2],
        [TableColumn3],
        [TableColumn4],
        [TableColumn5],
        [TableColumn6],
        [TableColumn7],
        [TableColumn8],
        [TableColumn9],
        [TableColumn10],
        [TableColumn11],
        [TableColumn12],
        [TableColumn13]
    )
    VALUES (
        @Value1,
        @Value2,
        @Value3,
        @Value4,
        @Value5,
        @Value6,
        @Value7,
        @Value8,
        @Value9,
        @Value10,
        @Value11,
        @Value12,
        default
    )
    SELECT SCOPE_IDENTITY() As InsertedID
END
ELSE BEGIN
    UPDATE YourTableName SET 
        [TableColumn1] = @Value1,
        [TableColumn2] = @Value2,
        [TableColumn3] = @Value3,
        [TableColumn4] = @Value4,
        [TableColumn5] = @Value5,
        [TableColumn6] = @Value6,
        [TableColumn7] = @Value7,
        [TableColumn8] = @Value8,
        [TableColumn9] = @Value9,
        [TableColumn10] = @Value10,
        [TableColumn11] = @Value11,
        [TableColumn12] = @Value12,
        [TableColumn13] = @Value13
    WHERE [TableColumn] = @Value

END
GO`

2 Comments

When you post sample code as an answer, please try to make the sample code small enough that it's easy to focus on which part is the answer. Also, you are answering a question that is more than 3 years old and which has an accepted answer already.
@MarcTalbot : thank you and exactly you are right.. as you see i am not expert on these subjects.. Sorry for mistake.. in future i' ll try to care on these rules.. Thank you again.. Regards;
0

With enough defaults on a table, you can simply say:

INSERT t DEFAULT VALUES

Note that this is quite an unlikely case, however.

I've only had to use it once in a production environment. We had two closely related tables, and needed to guarantee that neither table had the same UniqueID, so we had a separate table which just had an identity column, and the best way to insert into it was with the syntax above.

Comments

-1

The most succinct solution I could come up with is to follow the insert with an update for the column with the default:

IF OBJECT_ID('tempdb..#mytest') IS NOT NULL DROP TABLE #mytest
CREATE TABLE #mytest(f1 INT DEFAULT(1), f2 INT)
INSERT INTO  #mytest(f1,f2) VALUES (NULL,2)
INSERT INTO  #mytest(f1,f2) VALUES (3,3)

UPDATE #mytest SET f1 = DEFAULT WHERE f1 IS NULL

SELECT * FROM #mytest

Comments

-2

The pattern I generally use is to create the row without the columns that have default constraints, then update the columns to replace the default values with supplied values (if not null).

Assuming col1 is the primary key and col4 and col5 have a default contraint

-- create initial row with default values
insert table1 (col1, col2, col3)
    values (@col1, @col2, @col3)

-- update default values, if supplied
update table1
    set col4 = isnull(@col4, col4),
        col5 = isnull(@col5, col5)
    where col1 = @col1

If you want the actual values defaulted into the table ...

-- create initial row with default values
insert table1 (col1, col2, col3)
    values (@col1, @col2, @col3)

-- create a container to hold the values actually inserted into the table
declare @inserted table (col4 datetime, col5 varchar(50))

-- update default values, if supplied
update table1
    set col4 = isnull(@col4, col4),
        col5 = isnull(@col5, col5)
    output inserted.col4, inserted.col5 into @inserted (col4, col5)
    where col1 = @col1

-- get the values defaulted into the table (optional)
select @col4 = col4, @col5 = col5 from @inserted

Cheers...

Comments

-3

The easiest way to do this is to modify the table declaration to be

CREATE TABLE Demo
(
    MyColumn VARCHAR(10) NOT NULL DEFAULT 'Me'
)

Now, in your stored procedure you can do something like.

CREATE PROCEDURE InsertDemo
    @MyColumn VARCHAR(10) = null
AS
INSERT INTO Demo (MyColumn) VALUES(@MyColumn)

However, this method ONLY works if you can't have a null, otherwise, your stored procedure would have to use a different form of insert to trigger a default.

3 Comments

I tried this method and received the error: 'column does not allow nulls'.
Are you sure that the default is assigned on the column? I've this this method befoe.
I'm positive. I even took your sample code and still had the same error.
-4

The questioner needs to learn the difference between an empty value provided and null.

Others have posted the right basic answer: A provided value, including a null, is something and therefore it's used. Default ONLY provides a value when none is provided. But the real problem here is lack of understanding of the value of null.

.

3 Comments

If the column is nullable, yes. If the column is NOT nullable then...well...we all know what should happen, even if that is not what Celko says should happen.
The question he really wants to solve is why does he have to write an IF statement to use a default in a sproc in T-SQL, or define that default value in two places.
Perhaps one of these years someone will wander along to this question on stackoverflow, and post the elegant solution that nobody here knows about.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.