【问题标题】:Set-based approach to updating multiple tables, rather than a WHILE loop?基于集合的方法来更新多个表,而不是 WHILE 循环?
【发布时间】:2015-01-03 07:11:57
【问题描述】:

显然我太习惯于过程式编程,我不知道如何使用基于集合的方法来处理这个问题。

我在 SQL Server 中有几个临时表,每个表都有数千条记录。其中一些每个都有数万条记录,但它们都是记录集的一部分。我基本上是在加载一堆看起来像这样的 xml 数据:

<root>
    <entry>
        <id-number>12345678</id-number>
        <col1>blah</col1>
        <col2>heh</col2>
        <more-information>
            <col1>werr</col1>
            <col2>pop</col2>
            <col3>test</col3>
        </more-information>
        <even-more-information>
            <col1>czxn</col1>
            <col2>asd</col2>
            <col3>yyuy</col3>
            <col4>moat</col4>
        </even-more-information>
        <even-more-information>
            <col1>uioi</col1>
            <col2>qwe</col2>
            <col3>rtyu</col3>
            <col4>poiu</col4>
        </even-more-information>
    </entry>
    <entry>
        <id-number>12345679</id-number>
        <col1>bleh</col1>
        <col2>sup</col2>
        <more-information>
            <col1>rrew</col1>
            <col2>top</col2>
            <col3>nest</col3>
        </more-information>
        <more-information>
            <col1>234k</col1>
            <col2>fftw</col2>
            <col3>west</col3>
        </more-information>
        <even-more-information>
            <col1>asdj</col1>
            <col2>dsa</col2>
            <col3>mnbb</col3>
            <col4>boat</col4>
        </even-more-information>
    </entry>
</root>

下面是临时表的简要展示:

临时表1(条目)

+------------+--------+--------+
|  UniqueID  |  col1  |  col2  |
+------------+--------+--------+
|   732013   |  blah  |  heh   |
|   732014   |  bleh  |  sup   |
+------------+--------+--------+

临时表 2(更多信息)

+------------+--------+--------+--------+
|  UniqueID  |  col1  |  col2  |  col3  |
+------------+--------+--------+--------+
|   732013   |  werr  |  pop   |  test  |
|   732014   |  rrew  |  top   |  nest  |
|   732014   |  234k  |  ffw   |  west  |
+------------+--------+--------+--------+

临时表 3(更多信息)

+------------+--------+--------+--------+--------+
|  UniqueID  |  col1  |  col2  |  col3  |  col4  |
+------------+--------+--------+--------+--------+
|   732013   |  czxn  |  asd   |  yyuy  |  moat  |
|   732013   |  uioi  |  qwe   |  rtyu  |  poiu  |
|   732014   |  asdj  |  dsa   |  mnbb  |  boat  |
+------------+--------+--------+--------+--------+

我正在从一个 XML 文件加载这些数据,并且发现这是我可以分辨哪些信息属于哪条记录的唯一方法,因此每个临时表的顶部都插入了以下内容:

T.value('../../id-number[1]', 'VARCHAR(8)') UniqueID,

如您所见,每个临时表都有一个UniqueID 分配给它的特定记录,以表明它属于主记录。我在数据库中有大量项目,我想使用基于集合的方法更新每个非临时表中的每一列,但它必须受到UniqueID 的限制。

在第一个以外的表中,有一个基于主表的PrimaryKey_IDForeign_ID,并且不会插入UniqueID……只是为了帮助告诉什么去哪里。

这是我试图弄清楚的确切逻辑:

  1. 如果id-number 当前存在于主表中,则根据主表的PrimaryKey_ID 编号更新表,该编号与每个表的Foreign_ID 中的确切编号相同。外键表的编号与id-number 完全不同——它们不一样。

  2. 如果id-number 不存在,则插入记录。我已经完成了这部分。

但是,我目前的思维定势是我必须设置临时变量,例如@IDNumber@ForeignID,然后循环遍历它。我不仅得到了多个结果而不是当前结果,而且每个人都说不应该使用WHILE,尤其是对于如此大量的数据。

如何使用基于集合的方法更新这些表?

【问题讨论】:

  • XML 是否已经提取到临时表中(即以“#”开头)?
  • 是的。 #TempTable1、TempTable2 等等。其中有 15 个。

标签: sql-server xml


【解决方案1】:

假设您已经提取了这个 XML,您可以执行类似的操作:

UPDATE ent
SET    ent.col1 = tmp1.col1,
       ent.col2 = tmp1.col2
FROM   dbo.[Entry] ent
INNER JOIN #TempEntry tmp1
        ON tmp1.UniqueID = ent.UniqueID;

UPDATE mi
SET    mi.col1 = tmp2.col1,
       mi.col2 = tmp2.col2,
       mi.col3 = tmp2.col3
FROM   dbo.[MoreInformation] mi
INNER JOIN dbo.[Entry] ent -- mapping of Foreign_ID ->UniqueID
        ON ent.PrimaryKey_ID = mi.Foreign_ID
INNER JOIN #TempMoreInfo tmp2
        ON tmp2.UniqueID = ent.UniqueID
       AND tmp2.SomeOtherField = mi.SomeOtherField; -- need 1 more field

UPDATE emi
SET    ent.col1 = tmp3.col1,
       emi.col2 = tmp3.col2,
       emi.col3 = tmp3.col3,
       emi.col4 = tmp3.col4
FROM   dbo.[EvenMoreInformation] emi
INNER JOIN dbo.[Entry] ent -- mapping of Foreign_ID ->UniqueID
        ON ent.PrimaryKey_ID = mi.Foreign_ID
INNER JOIN #TempEvenMoreInfo tmp3
        ON tmp3.UniqueID = ent.UniqueID
       AND tmp3.SomeOtherField = emi.SomeOtherField; -- need 1 more field

现在,我应该指出,如果目标真的是

更新每个非临时表中的每一列

那么对于具有多条记录的任何子表都会存在概念问题。如果该表中没有在Foreign_ID 字段之外保持不变的记录(我猜该表的PK?),那么你怎么知道哪一行是更新的?当然,您可以根据非临时Entry 表中的UniqueID 映射找到正确的Foreign_ID,但至少需要有一个不是IDENTITY 的字段(或UNIQUEIDENTIFIER 通过@ 填充987654328@ 或 NEWSEQUENTIALID) 将用于查找确切的行。

如果无法找到稳定的匹配字段,那么您别无选择,只能使用擦除和替换方法。

附:我曾经推荐过MERGE 命令,但由于了解了所有的错误和问题,我已经停止了。 “更好”的语法不值得潜在的问题。更多信息请见Use Caution with SQL Server's MERGE Statement

【讨论】:

  • 我知道哪一行是基于每个表的 Foreign_ID 的。它将始终与第一个表的PrimaryKey_ID 匹配。条目表有一个PrimaryKey_ID,所有其他表都有一个Foreign_ID 键,它是相同的数字。
  • @heh 请再次阅读我的回答;-)。您的示例数据显示,对于 more-information 表,两个条目的 UniqueID732014。您如何知道这两行中的哪一行要更新非临时表中的哪一行(假设您更新了除 PK 和 Foreign_ID 字段之外的所有字段)?如果没有另一个字段缩小到单行,则两个现有行都将更新为临时表中的这两行之一。现在更有意义了吗?
  • @heh 你不理解这里的问题。我已经考虑了映射id-number -> PrimaryKey_ID -> Foreign_ID。这不是问题。更仔细地查看您的示例数据,特别是第一个 &lt;entry&gt; 元素。它有两个&lt;even-more-information&gt; 元素,它们都共享相同的id-number。更新数据库中的行时,需要匹配 PK 字段(或某些唯一字段)。
  • @heh 听起来你仍然很困惑,至少在最后。是的,相同&lt;entry&gt; 记录的XML 中的两个&lt;even-more-information&gt; 元素引用EvenMoreInformation 表中具有相同ForeignID 的两行。所以是的,该查询将为特定的[entry].[PrimaryKey_ID] 返回 2 行。那就是问题所在。您在 XML 中应该有两行,每行都应该更新真实表中的一行,但它们都会更新这两行,因为您没有提供足够的信息来缩小范围以更新 XML 中每个元素的单行。
  • @heh 是的,这就是我一直在说的,也是我在回答中的建议:如果您在 XML(id-number/UniqueID 之外)中没有任何字段,那么没有更改相关表之一,那么您别无选择,只能删除和插入(即擦除和替换),因为无法进行更新。
【解决方案2】:

您可以使用 MERGE 在单个语句中执行 upsert(更新和插入)

首先将条目合并到主表

对于其他表,你可以和主表做一个join来得到外来的id映射

MERGE Table2 as Dest
USING ( select t2.*, m.primaryKey-Id as foreign_ID
        from #tempTable2 t2 
        join mainTable m 
        on t2.id-number = m.id-number 
        ) as Source
on Dest.Foreign_ID = m.foreign_ID
WHEN MATCHED
   THEN Update SET Dest.COL1 = Source.Col1
WHEN NOT MATCHED then
   INSERT (FOREGIN_ID, col1, col2,...)
  values ( src.foreign_Id, src.col1, src.col2....)

【讨论】:

  • 您不需要创建单独的映射表,因为它已经以主“条目”表的形式存在。
猜你喜欢
  • 1970-01-01
  • 2016-06-07
  • 1970-01-01
  • 2020-05-15
  • 2015-01-04
  • 1970-01-01
  • 2016-05-28
  • 2020-07-17
  • 1970-01-01
相关资源
最近更新 更多