【问题标题】:Best practise when updating individual fields in a database record更新数据库记录中的单个字段时的最佳实践
【发布时间】:2009-10-28 15:10:17
【问题描述】:

我使用 .NET 创建了一个数据访问层。在我更新数据库记录的任何地方,我都使用了 sql

UPDATE Customer SET FirstName=:FirstName, LastName=:LastName, Address1=:Address1, Address2=:Address2,....等

这意味着记录中的每个字段都会更新,即使可能只有一个字段已更改。一位同事对此提出了质疑,说我们应该只在字段发生变化时更新它,并引用带宽作为问题 - 假设我们有 160 个字段,然后我们传递 160 个字段的数据。如果我检查一个值是否发生了变化,我想我可以节省 Web 服务器和数据库服务器之间的带宽,并完全根据实际更改的值生成 sql。

在 Web 服务器和客户端之间,我现在需要同时传递旧值和新值,因此我可能会增加那里的带宽(但无论如何 ASP.NET 已经这样做了,我不确定我们可以切换那个位关闭,所以这可能不是问题)。

那么最佳实践是什么?我应该担心更新数据库记录中的所有字段吗?我将如何只更新已更改的字段?

10 月 29 日添加的编辑:有人知道 NHibernate 是做什么的吗?也许这是花时间学习如何做到这一点的论据。

【问题讨论】:

    标签: .net sql database


    【解决方案1】:

    过早的优化是万恶之源。

    这有什么理由吗,你真的有带宽问题吗?

    根据我的经验,跟踪更改可能需要大量工作(即需要维护大量代码),具体取决于其实现方式。有一些优雅的方法,但我认为你正在做的事情很好,除非有一些证据支持改变。

    如果您计划实施某种自定义形式的变更跟踪,我当然不会与数据库“聊天”。当您第一次从数据库中提取对象并与之进行比较时,请在本地制作一个副本。

    【讨论】:

    • 我主要使用 TableAdapters,我还没有调查他们是否已经实现了更改跟踪,但我认为我正在转向使用我自己的自定义 DataTransfer 对象,所以如果有一个优雅的方式要实施它,我会很感兴趣。你能指点我那个方向吗?
    【解决方案2】:

    也许除了带宽之外,您还可能需要考虑多用户争用。根据您的应用程序的设计方式,更新所有字段的方法可能会导致问题。

    如果您有两个用户同时加载第 1 条记录。用户 A 将姓氏从“Smith”更改为“Smythe”并保存。然后用户 B 将 Address1 从“The Street”更改为“Any Street”并保存。

    根据您的方法,第二次保存还会将 LastName 再次恢复为“Smith”(因为这是用户 B 加载它时的状态)。

    按照逻辑得出结论,您还需要在保存时进行其他检查(可能是某种时间戳),以确保数据库中的值与您认为的一致。

    【讨论】:

    • 我认为这是一个非此即彼的情况。更新所有字段时,您需要时间戳或版本号来实现乐观并发。使用生成的 sql 将大大降低 2 个用户更新同一字段的风险,但不能消除它。要消除它,您需要旧值:更新表集 field1=newvalue where field1=oldvalue。然后我们回到带宽参数,因为我们将旧值和新值传递给数据库,而不仅仅是新值........
    • 数据完整性和带宽哪个更重要?对我来说,这个问题的答案没有争议,尽管也有例外。
    • 所以你会争辩说我们应该总是实现并发检查。在这种情况下,我们不太可能使用生成的 sql 来节省大量带宽,因为我们需要将旧值以及新值传递给数据库。我认为带宽论点正在失势.....
    【解决方案3】:

    假设您在 Web 服务器和数据库服务器之间有千兆网络连接,那么您必须在每秒 100 兆字节的数据区域内更新 somwhere 才能达到接近是个问题。这大约是每天 8 TB。我的猜测是你没有更新这么多数据,所以这可能不是问题。

    【讨论】:

      【解决方案4】:

      网络带宽在各个层面都在不断提高。除非您在某处合法地遇到瓶颈,否则不要担心。即便如此,我的预感是增益足够小,以至于您将花费更多的时间和精力(和风险)来尝试优化您的更新,而不是在其他地方找到性能改进。

      【讨论】:

        【解决方案5】:

        好吧,你要么按照你以前的方式来做(它有效,并且每次都传递每个字段),要么花时间为每个字段生成定制的 SQL,然后为每个字段传递一个 SQL 语句,或者生成一个巨大的动态的一些 SQL 只包含更新的字段(如果你犯了错误,会有 SQL 注入的风险)。通过在参数化存储过程中进行更新并且只传递字段值而不是名称,您可能会获得更好的带宽节省。

        除非带宽真的有问题,否则我认为您没有问题。无论如何,SP 路线是一个更好的选择。

        【讨论】:

        • 我会在我的 SP 中放入什么?为每个领域定制 SQL?生成的sql?在我的代码中这样做不是更好吗?我仍然可以使用参数化查询来避免 sql 注入。
        • 在代码中,你必须做完整的更新语句(Field1 = Value1, Field2 = Value2...)然后通过它。使用 SP,您只需传递 SP 名称和参数信息。
        • 啊啊!所以你说如果我有 100 个字段并且每个字段名称是 10 个字符长,我可以节省超过 2000 个字符的带宽。我以前没有真正考虑过的 SP 的优势。
        【解决方案6】:

        其中一个字段不是表的 PK 吗?你真的更新PK了吗?外键呢?你也在更新那些?

        问题不在于带宽。至少隔离记录的非关系元素并只更新该集合(不包括 PK 和外键)。

        【讨论】:

        • Ok 那么如果将外键或主键更新为相同的值,会有什么问题呢?我不认为我曾经更新主键,但外键可能会改变。在这种情况下你会推荐什么?
        • PK 更改不适用于 UPDATE,因为您会收到错误消息。人们会期望改变外键关系在应用程序/域语义方面是相当不同的,而不仅仅是更新描述性值。因此,问题在于模糊了意图并可能引入了细微的错误。
        • 我刚刚测试了更新主键。我在 Oracle 中没有收到错误,而且我确定我也在 sql server 中完成了它。 (不是我特别想这样做!)我猜数据库将不得不重新调整索引,这可能是不可取的 - 但不比删除后插入更糟糕吗?恐怕我不明白你所说的外键是什么意思。我认为我们在主表中讨论的大多数外键实际上只是查找表。因此,如果客户搬到另一个国家/地区,而我将国家/地区存储为查找,为什么不将外键与 Address1 一起更新?
        • @alphazero:是什么让您认为您无法使用 UPDATE 语句更新主键。有很多应用程序至少对某些表使用有意义的 PRIMARY KEY,如何维护它们?如果 PK 永远无法更新,为什么 SQL 会指定 ON CASCADE 行为?
        【解决方案7】:

        我发现自己处于同样的境地。我编写了自己的轻量级 ORM/DAL 来连接非优化设计的数据库,即具有 200 多个字段的表。

        我发现尝试更新一行的所有字段确实存在开销,而不仅仅是那些已更改的字段。

        在我的情况下,我能够通过对 ORM 属性使用智能“设置器”来解决问题,如果尝试修改特定字段,它将记录。这意味着当我将对象提交回数据库时,我可以发出一条 SQL UPDATE 语句,它只引用那些标记为已修改的字段。

        注意对于 SELECT 语句,最佳实践要求您应该明确引用要返回的字段,而不是使用“SELECT * FROM”。据说这是出于性能/带宽的原因,因此反过来对 UPDATES 有意义。

        【讨论】:

        • 我们的平均表包含大约 8 列,我们最大的表是 33 列。有没有一个你不会担心的级别,或者你现在总是实现智能设置器?
        • 我总是实现智能设置器,因为它们现在是我自制的 ORM/DAL 的一部分,所以我可以免费获得它们。在您的特定情况下,鉴于您的平均列数,我不建议您实施智能设置器,因为您可能看不到可测量的差异。当然,除非您的大部分更新可能针对 33 列表。
        猜你喜欢
        • 2010-10-11
        • 2011-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-19
        • 1970-01-01
        • 1970-01-01
        • 2013-08-18
        相关资源
        最近更新 更多