当参数为null时插入默认值

时间:2008-10-23 17:34:50

标签: sql sql-server tsql stored-procedures null

我有一个包含默认值的列的表:

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

我正在使用存储过程将值插入此表中:

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

问题是,当@valuenull时,如何让它使用默认值?我试过了:

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

这显然不起作用。还尝试了case声明,但这也不太公平。还有其他建议吗?我是以错误的方式解决这个问题吗?

更新:我正在尝试完成此,不必须:

  1. 在多个位置维护default值,
  2. 使用多个insert语句。
  3. 如果这是不可能的,那么我想我只需忍受它。似乎这应该是可以实现的。

    注意:我的实际表有多个列。我只是在写一个例子。

17 个答案:

答案 0 :(得分:12)

尝试if语句......

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

答案 1 :(得分:10)

克里斯托夫,

仅当您未在INSERT语句中指定列时才应用列的默认值。

由于您明确列出了insert语句中的列,并且明确地将其设置为NULL,因此它会覆盖该列的默认值

您需要做的是“如果将null传递到您的sproc中,则不要尝试为该列插入”。

这是一个快速而令人讨厌的例子,说明了如何使用一些动态的SQL。

创建一个包含默认值的列的表...

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

创建一个SPROC,它根据输入参数

动态构建和执行insert语句
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

以下两个命令应该为您提供输出所需的示例...

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

我知道这是一种非常复杂的方式来做你需要做的事情。 您可以同样从InformationSchema中为相关列选择默认值,但说实话,我可能会考虑将默认值添加到过程顶部的param

答案 2 :(得分:3)

据我所知,只有在insert语句中没有指定值时才会插入默认值。因此,例如,您需要在包含三个字段的表中执行以下操作(value2默认)

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

然后value2将被默认。也许有人会在如何为一个单一字段的表格中实现这一目标。

答案 3 :(得分:2)

chrisofspades,

据我所知,行为与数据库引擎的工作方式不兼容, 但是有一个简单的(我不知道是否优雅但高效)解决方案来实现你的两个目标不要

  1. 在多个位置维护默认值,
  2. 使用多个插入语句。
  3. 解决方案是使用两个字段,一个可插入用于插入,另一个用于计算选择:

    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
    

    这种方法甚至可以让您将业务默认值的管理集中在一个参数表中,放置一个ad hoc函数来执行此操作,vg更改:

    selValue AS ISNULL(insValue, 'something')
    

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

    我希望这会有所帮助。

答案 4 :(得分:2)

这是我能想到的最好的。它可以防止sql注入只使用一个insert语句,并且可以使用更多case语句进行扩展。

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

答案 5 :(得分:2)

可能不是性能最友好的方式,但您可以创建一个标量函数,该函数使用表和列名从信息模式中提取,然后使用您之前尝试过的isnull逻辑调用它:

    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

然后像这样称呼它:

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

答案 6 :(得分:1)

您可以对存储过程的参数使用默认值:

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

END

如果未提供@ MyParam2,则它将具有'ABC'值...

答案 7 :(得分:0)

您可以在MS SQL中使用COALESCE功能。

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

就个人而言,我并不为这个解决方案而疯狂,因为如果你想改变默认值,这是一个维护噩梦。

我的偏好是Mitchel Sellers的提议,但这在MS SQL中不起作用。无法与其他SQL dbms对话。

答案 8 :(得分:0)

希望帮助-newbie,因为我在MSSQL中使用Upsert语句。(这个代码我在MSSQL 2008 R2的项目中使用并且工作简单完美......可能不是最佳实践..执行时间统计信息显示执行时间为15毫秒(使用insert语句)

只需将列的“默认值或绑定”字段设置为您决定用作列的默认值的字段,并将列设置为“不接受设计菜单中的空值”并创建此存储过程..

`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) 

- 在我的项目中@ Value13是一个照片列,存储为字节数组.. - 我计划在没有照片通过时使用默认照片 - 到sp存储在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`

答案 9 :(得分:0)

如果桌子上有足够的默认值,您可以简单地说:

INSERT t DEFAULT VALUES

但请注意,这是一个非常不可能的情况。

我只需要在生产环境中使用一次。我们有两个密切相关的表,并且需要保证两个表都没有相同的UniqueID,所以我们有一个单独的表只有一个标识列,插入其中的最好方法是使用上面的语法。

答案 10 :(得分:0)

插入时不要指定列或值,DEFAULT constaint的值将替换缺失值。

我不知道这在单个列表中如何工作。我的意思是:它会,但它不会非常有用。

答案 11 :(得分:0)

我能提出的最简洁的解决方案是使用默认值对列进行更新:

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

答案 12 :(得分:0)

到目前为止,最好的选择是为表创建一个INSTEAD OF INSERT触发器,从表中删除默认值,并将它们移动到触发器中。

这将如下所示:

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

这使得它无关紧要是什么代码尝试将NULL插入到表中,避免存储过程,完全透明,并且您只需要在一个地方维护默认值,即此触发器。

答案 13 :(得分:0)

我通常使用的模式是创建不包含具有默认约束的列的行,然后更新列以使用提供的值替换默认值(如果不为null)。

假设col1是主键,而col4和col5具有默认约束

-- 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

如果要将实际值默认为表...

-- 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

干杯...

答案 14 :(得分:0)

辅助表ejemplo。 检修程序 Ejemploejecución:缺省DE SPULT en el paso al SP。

SP的标记结果,最终结果得到确认。 最低要求插入和插入值(默认) 默认版本与“默认”(逗号)不同

    create Table Cliente_SeguimientoDeuda(IDRegistro int identity(1,1), Cliente int, ReferenciaReclamacion NVarChar(20) Default 'Reclamación', 
    Fecha DateTime, PersonaContactada nvarchar(30), Causa NVarChar(50), CompromisoObtenido NvarChar(50), 
    Usuario NVarChar(50), NotasReclamacion NVarChar(500),
    Primary Key (IDRegistro))


    create procedure NET_ADDReclamacion(@Cliente int, @ReferenciaReclamacion nvarchar(20)='por defecto' , 
    @Fecha datetime, @PersonaContactada nvarchar(30),@Causa nvarchar(50), @Compromiso nvarchar(50)
    , @Usuario nvarchar(50), @NotasReclamacion nvarchar(500))
    as
    set nocount on
    insert into Cliente_SeguimientoDeuda(Cliente, ReferenciaReclamacion,Fecha, PersonaContactada,Causa,CompromisoObtenido,
    Usuario, NotasReclamacion) Values (@Cliente,@ReferenciaReclamacion,@Fecha,@PersonaContactada,@Causa,@Compromiso,
    @Usuario,@NotasReclamacion)
    select @@IDENTITY

NET_ADDReclamacion 1,default,'01-01-2019','roberto','causa','compromiso','usu','notas'

答案 15 :(得分:-2)

提问者需要了解提供的空值与null之间的区别。

其他人已经发布了正确的基本答案:提供的值(包括null)是某种东西,因此它被使用了。默认值仅在未提供时提供值。但这里真正的问题是缺乏对null值的理解。

答案 16 :(得分:-2)

最简单的方法是将表声明修改为

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

现在,在您的存储过程中,您可以执行类似的操作。

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

但是,此方法仅在您不能为null时才有效,否则,您的存储过程必须使用不同形式的insert来触发默认值。