【问题标题】:Is there a way to conditionally use default column values in an INSERT..SELECT statement?有没有办法在 INSERT..SELECT 语句中有条件地使用默认列值?
【发布时间】:2010-03-02 20:41:54
【问题描述】:

我在一个向表中插入一行的存储过程中有类似于以下的代码,我想将最后一列(FieldD)设置为@prmSomeValue,除非它为空,否则只需使用默认值为该列定义。

IF (@prmSomeValue IS NULL)
   INSERT INTO MyTable (fieldA,FieldB,FieldC)  
      SELECT A,B,C 
      FROM MyOtherTable
ELSE
   INSERT INTO MyTable (fieldA,FieldB,FieldC,FieldD)  
      SELECT A,B,C,@prmSomeValue 
      FROM MyOtherTable

这可行,但违反了 DRY 原则。我正在尝试使用单个插入语句找到一些方法来做到这一点。类似于以下伪代码的内容。

   INSERT INTO MyTable (fieldA,FieldB,FieldC,FieldD)  
      SELECT A,B,C,ISNULL(@prmSomeValue,DEFAULT)
      FROM MyOtherTable

有人有什么想法吗?

更新 - 更多变化
默认约束不是文字值,而是如下所示的函数。

...DEFAULT (suser_sname()) FOR [FieldD]

更新
我终于下注并选择了较小的弊端,只是将默认值函数复制到我的查询中,而不是使用为列配置的默认值。我不喜欢它,但它可以在我的查询中减少重复次数。

   INSERT INTO MyTable (fieldA,FieldB,FieldC,FieldD)  
      SELECT A,B,C,ISNULL(@prmSomeValue,suser_sname())
      FROM MyOtherTable

【问题讨论】:

    标签: sql tsql sql-server-2008 insert column-defaults


    【解决方案1】:

    由于本质上这就是 SQL Server 正在做的事情,您可以这样做至少避免两个几乎相同的语句(伪代码):

    INSERT (columnA,B,C) ... ;
    
    IF @prmSomeValue IS NOT NULL
        UPDATE ... ;
    

    我认为没有办法使用默认值进行 COALESCE。

    【讨论】:

    • 有趣的想法。不确定它是否更可取,但绝对值得一票。
    • 感谢 JohnFx,我认为一旦有多个可选列以这种方式处理,它可能会更可取。然后你可以说“IF @foo IS NOT NULL OR @bar IS NOT NULL, UPDATE SET foo = COALESCE(@foo, foo), bar = COALESCE(@bar, bar) WHERE”等等,而不是写一个插入语句对于每一种可能的组合。
    • 使用 ISNULL 代替 COALESCE,COALESCE 用于多个参数。我不会使用 field = parameter / field 方法,它会降低 SQL 性能,使得优化查询和索引的使用变得更加困难。一个简单的 if 语句会更有效率。
    • 同意。但是,我的情况此时只需要一个列。此外,它会变得更复杂一些,因为真正的查询还使用了我在简化示例中省略的 OUTPUT INTO 子句。
    • Zyphrax,我不同意 ISNULL()。我总是使用 COALESCE() ,因为如果我突然需要一个额外的参数,我不应该更改我的代码。反对 ISNULL() 的另外两件事:它不是 ANSI 标准,并且与其他语言(VB、Access)中的相同 ISNULL() 函数的行为不同,因此可能会让来自这些地方的用户感到困惑。
    【解决方案2】:

    我会说你的方法很好。一个简单的检查,然后是一个插入。如果担心 DRY,封装调用,以便重复调用。

    我想说,在某些表上插入/更新数据库可能代价高昂(取决于设计目标),因此如果您必须编写额外的代码来处理这种情况,那么我认为权衡没有问题。

    【讨论】:

    • +1,最好尝试防止重复的代码块。然而,许多程序员正试图将他们的 OO 原则和编码实践应用于 T-SQL。通常会导致漂亮但低效的查询。有时编写 5 个非常相似的查询而不是 1 个半动态的查询对性能很有好处 - 保持代码不变,它集中在一个存储过程中,易于维护。
    • 你不知道他们把这些东西打到了我们的脑海中,以至于在条件的两端复制几乎相同的代码会造成身体伤害吗? =) 你可能是对的,也许我想多了。
    • 当您只有 A 或 B 时,我同意按原样可能没问题。但是当你有四个这样的列时会发生什么?根据哪些参数为 NULL,您需要的可能 INSERT 语句的数量很快就会激增。有时您必须平衡效率和维护。
    • @aaron。在大多数情况下,绝对维护是最重要的
    【解决方案3】:

    可能工作,取决于您是指在默认约束中定义的默认值,还是在代码中定义的默认值? 如果“约束”它失败,如果“代码”它工作。 编辑:你的意思是约束。呵呵!

    SELECT A,B,C,@prmSomeValue
          FROM MyOtherTable
          WHERE @prmSomeValue IS NOT NULL
    UNION ALL
    SELECT A,B,C,DEFAULT
          FROM MyOtherTable
          WHERE @prmSomeValue IS NULL
    

    无论你想怎么做,在 INSERT 子句中指定一个列都需要一个值。

    所以你的第一个解决方案就是你所做的......

    【讨论】:

    • 我的意思是为列定义的默认约束。就像我的 IF 语句的前半部分一样。所以这行不通。注意:并非绝对需要在 INSERT 子句中指定列。如果有必要完成这项工作,我可以依赖列排序,尽管我不想这样做。
    • JohnFx,如果省略列列表,则需要让后续选择具有与表匹配的相同数量的列,否则您将得到:“Msg 213, Level 16 , 状态 1, Line 1 列名或提供的值的数量与表定义不匹配。”即使这确实有效,我也会非常谨慎地依赖列顺序(您的直觉是正确的,恕我直言)。
    【解决方案4】:

    这样的东西可能会起作用(虽然不是很漂亮):

    INSERT INTO MyTable (fieldA,FieldB,FieldC,FieldD)  
    SELECT A,B,C,
        case when @prmSomeValue is null 
    then
            (SELECT text FROM syscomments WHERE id IN (SELECT cdefault FROM syscolumns
                WHERE id = object_id('MyTable') AND cdefault > 0))
        else @prmSomeValue
        end
    FROM MyOtherTable
    

    【讨论】:

    • 如果默认值实际上是一个公式(例如 GETDATE() 或 NEWID())会发生什么?它将被解释为字符串,从而导致错误或不正确的数据,具体取决于列的数据类型。您是否也不想确保获得正确的列(cdefault > 0 的列可能不止一列,导致 Msg 512 - 子查询返回多个值)?对于 SQL Server 2008,您不应该使用目录视图而不是已弃用的系统表吗?
    • 我在考虑类似的方法,但有点担心与使用这样的系统表相关的副作用或权限问题,但我想它感觉足够可靠。对这种技术有什么注意事项吗?
    • 噢!这种方法的一个主要问题。如果默认是一个函数,这只是插入函数的名称,在我的例子中是“getdate()”。不错的尝试。
    • 是的,JohnFx,您没有看到我发布的警告吗?
    • 对不起,我在看到你的第一条评论之前写了这个。对不起。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-23
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    • 2010-10-17
    相关资源
    最近更新 更多