46

我有一个表,其中有一列具有默认值:

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

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

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

问题是,我如何让它使用默认值 when @valueis null?我试过了:

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

那显然行不通。还尝试了一个case声明,但这也不公平。还有其他建议吗?我会以错误的方式解决这个问题吗?

更新:我试图做到这一点不必:

  1. default在多个地方保持价值,以及
  2. 使用多个insert语句。

如果这是不可能的,那么我想我只能忍受它。似乎这应该是可以实现的。

注意:我的实际表格不止一列。我只是在快速写一个例子。

4

16 回答 16

18

克里斯托夫,

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

由于您在插入语句中明确列出该列,并明确将其设置为 NULL,这将覆盖该列的默认值

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

这是一个快速而讨厌的例子,说明如何使用一些动态 sql 来做到这一点。

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

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

创建一个基于输入参数动态构建和执行插入语句的 SPROC

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

以下 2 个命令应该为您提供一个示例,说明您想要什么作为输出......

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

于 2008-10-23T18:34:42.737 回答
11

试试 if 语句...

if @value is null 
    insert into t (value) values (default)
else
    insert into t (value) values (@value)
于 2008-10-23T18:04:24.373 回答
4

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

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

然后 value2 将被默认。也许有人会插话如何为具有单个字段的表完成此操作。

于 2008-10-23T17:39:26.403 回答
2

黑桃,

据我所知,该行为与 db 引擎的工作方式不兼容,但是有一个简单的(我不知道是否优雅,但性能良好)的解决方案可以实现您的两个目标

  1. 在多个位置保持默认值,并且
  2. 使用多个插入语句。

解决方案是使用两个字段,一个可以为空插入,另一个计算选择:

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

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

selValue AS ISNULL(insValue, 'something')

为了

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

我希望这有帮助。

于 2014-08-31T09:59:52.757 回答
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') )
于 2008-10-23T22:34:02.800 回答
2

这是我能想到的最好的。它可以防止 sql 注入只使用一个插入语句,并且可以使用更多的 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
于 2012-04-25T20:03:33.630 回答
2

到目前为止,最好的选择是为您的表创建一个 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 插入到您的表中,避免存储过程,完全透明,并且您只需要在一个地方维护您的默认值,即此触发器,它都可以工作。

于 2017-04-14T11:44:16.130 回答
1

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

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

END

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

于 2008-10-23T17:39:52.783 回答
0

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

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

于 2008-10-23T17:40:13.640 回答
0

您可以在 MS SQL 中使用 COALESCE 函数。

插入 t ( 值 ) 值( COALESCE(@value, 'something') )

就个人而言,我对这个解决方案并不感兴趣,因为如果你想更改默认值,这将是一场维护噩梦。

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

于 2008-10-23T21:32:28.367 回答
0

表上有足够的默认值,您可以简单地说:

INSERT t DEFAULT VALUES

但是请注意,这种情况不太可能发生。

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

于 2012-05-31T09:51:26.637 回答
0

希望能帮助 - 像我一样的新手 - 在 MSSQL 中使用 Upsert 语句的人 ..(我在 MSSQL 2008 R2 的项目中使用的这段代码非常完美..可能不是最佳实践.. 执行时间统计显示执行插入语句的时间为 15 毫秒)

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

`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 存储在数据库中..

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`
于 2012-02-20T12:47:19.990 回答
-1

我通常使用的模式是创建没有具有默认约束的列的行,然后更新列以用提供的值替换默认值(如果不是 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

干杯...

于 2018-09-26T16:06:15.513 回答
-1

我能想到的最简洁的解决方案是在插入之后使用默认值更新列:

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
于 2016-02-24T21:41:51.470 回答
-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 时才有效,否则,您的存储过程将不得不使用不同形式的插入来触发默认值。

于 2008-10-23T17:50:12.637 回答
-4

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

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

.

于 2008-10-23T20:32:12.123 回答