【问题标题】:Equivalent of C# 'readonly' for an MS SQL column?相当于 MS SQL 列的 C#“只读”?
【发布时间】:2011-10-18 08:16:02
【问题描述】:

假设Products 表中有一个Price 列,价格可能会发生变化。
我可以更改它,但我想将原始 Price 值存储在另一列中

MS SQL 服务器是否可以自动执行此操作?

我可以使用 默认值 字段吗?
我必须声明触发器吗?

更新

我尝试使用Price 来简化问题,但看起来这引发了“使用单独的表格”类型的答案。
对我造成的混乱,我深表歉意。

在现实世界中,我需要存储一个外键 ID,我 100% 只需要 currentoriginal .

更新 2

我对建议的不同方法感到有些困惑,所以请让我再次解释一下情况。

虚构的Products 表具有三个字段:IDPriceOriginalPrice
我想在任何插入上将OriginalPrice 设置为Price

有时它是由代码创建的单一产品。有时,一个存储过程中的单个insert 创建了数千种产品,所以我也想正确处理这些产品。

一旦设置了OriginalPrice,我就不再打算更新它了。

希望我的问题现在更清楚了。
感谢您的努力。

最终更新

我要感谢大家,特别是 @gbn,感谢他们的帮助。
虽然我发布了my own answer,但它主要基于@gbn's answer 和他的进一步建议。他的回答也比较完整,所以我标记为正确。

【问题讨论】:

    标签: sql sql-server sql-server-2008 default-value alter-table


    【解决方案1】:

    更新后,假设您只有旧值和新值。

    如果由于客户端代码错误而快速连续发生相同的更新并且您对历史不感兴趣(其他答案),请忽略

    您可以使用触发器或存储过程。

    就个人而言,我会使用存储过程来提供基本的控制。然后不需要直接的 UPDATE 权限,这意味着您通过您的代码只读除非

      CREATE PROC etc
      ...
      UPDATE
        MyTable
      SET
        OldPrice = Price,
        Price = @NewPrice,
        UpdatedBy = (variable or default)
        UpdatedWhen = DEFAULT --you have a DEFAULT right?
      WHERE
        PKCol = @SomeID
        AND --provide some modicum of logic to trap useless updates
        Price <> @NewPrice;
    

    触发器会类似,但您需要与 INSERTED 和 DELETED 表进行 JOIN 如果有人直接更新 OldPrice 怎么办?

      UPDATE
        T
      SET
        OldPrice = D.Price
      FROM
        Mytable T
        JOIN
        INSERTED I ON T.PKCol = I.PKCol
        JOIN
        DELETED D ON T.PKCol = D.PKCol
      WHERE
        T.Price <> I.Price;
    

    现在你明白为什么你会跳上...吗?

    问题编辑后,仅限INSERT

      UPDATE
        T
      SET
        OriginalPrice = I.Price
      FROM
        Mytable T
        JOIN
        INSERTED I ON T.PKCol = I.PKCol
    

    但是如果所有的 INSERT 都是通过存储过程发生的,我会在此处设置它....

    【讨论】:

    • 是的,我愿意!干杯 gbn。我想我可能误解了触发器的工作原理。我认为触发器只会在Inserted 表中包含 1 条记录,因为如果更新了多条记录,触发器将运行多次。好像不是这样的?
    • 我编辑了我的问题以使其更清楚。你能看一下吗?非常感谢您的时间和精力,谢谢。
    • 感谢您的第二个样品。如果我使用触发方式,我需要使其成为for insert吗?对吗?
    • 我感到困惑的原因是我认为for insert 触发器没有deleted 表。
    • @Dan Abramov:现在您已经声明“插入时的价格,永远不会更新”,这更清楚了。您原来暗示的“当前”和“以前”(这是米奇的回答)
    【解决方案2】:

    SQL Server 表列没有只读属性。但是您可以使用触发器(并限制权限)实现您描述的功能

    除了,这不是解决问题的最佳方法。而是将价格视为类型 2 'slowly changing dimension'. 这涉及拥有一个 'ValidTo' 列(os 'StartDate' 和 'EndDate' 列),并关闭一条记录:

    Supplier_Key    Supplier_Code   Supplier_Name   Supplier_State  Start_Date  End_Date
    123           ABC             Acme Supply Co    CA            01-Jan-2000   21-Dec-2004
    124           ABC             Acme Supply Co    IL            22-Dec-2004   
    

    如果您确实走触发器的路线(我建议您使用 SCD 类型 2),请确保它可以处理多行:Multirow Considerations for DML Triggers

    【讨论】:

    • 感谢您在此线程中的输入。你能看看我发布的最新更新吗?我想我终于把我的问题说清楚了(我希望如此..)
    【解决方案3】:

    我建议将您的价格存储在一个名为 Prices 的单独表中,其中包含 PriceDate 列。

    然后,每当价格更新时,INSERT 就会在Prices 表中添加一条新记录。然后当你需要知道当前价格时,你可以从那里拉。


    但是,如果您希望自动更新 OriginalPrice 列,您可以在表中添加 TRIGGER 来执行此操作:

    http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx

    【讨论】:

    • 谢谢。我应该澄清一下——Price 只是一个例子,在我的实际问题中,我需要存储一个原始外键 ID,并且可以肯定我只需要访问当前值和原始值。
    • 啊,在那种情况下,请参阅我关于触发器的部分答案
    • 我认为这最好作为类型 2 '缓慢变化的维度'来处理
    • @Curt:游标代码有很大缺陷:所有游标都必须能够处理多行。
    • @Curt:如果是这样的话,恐怕你一直在写坏代码。任何触发器都必须能够处理多行。 msdn.microsoft.com/en-us/library/ms190752.aspx
    【解决方案4】:

    这就是我在@gbn@Mitch@Curt 的大力帮助下得到的结果:

    create trigger TRG_Products_Price_I --common-ish naming style
        on dbo.Products after insert as
    begin
        set nocount on
    
            update m
            set OriginalPrice = i.Price 
            from Products p
            join inserted i on p.ID = i.ID
    
    end
    

    我也尝试关注this article
    谢谢大家!

    【讨论】:

    • 对于任意数量的行,您只需要一个条件。并命名
    • 谢谢。我很快就会测试它。还有一个问题:这是否意味着我必须允许 OriginalPrice 为空值?显然,触发器在插入之后运行,尽管我确定最终设置了值..
    • 要么在列上设置默认值(比如零),要么将其设为 NULL。就个人而言,我会将其设为 NULL
    猜你喜欢
    • 1970-01-01
    • 2013-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    相关资源
    最近更新 更多