我有两个表,我需要能够同时更新它们,第一个表是项目列表:
ITEMS
Item* | Rev* | RDate | ECO | New
------+------+--------------+------+----
A | 0A | 2019-01-01 | E123 | 1
A | 01 | 2018-01-01 | E456 | 0
B | 0A | 2018-12-31 | E765 | 0
C | 01 | 2018-10-25 | E456 | 0
第二个是带有修订的父子表,但是我必须从Item表中填写Child Rev
树
Parent* | ParentRev* | Child* | ChildRev | VDate*
--------+------------+--------+----------+-----------
Y | 0B | C | NULL | 2019-01-01
Y | 0C | D | NULL | 2019-01-13
Z | 01 | A | NULL | 2018-06-25
Z | 02 | A | NULL | 2019-01-11
Z | 0A | B | NULL | 2019-01-01
注释:
VDate
不应该是主键的一部分,但是数据集是错误的并且有重复项,因此我需要添加它我研究了诸如Select first row in each GROUP BY group?之类的不同问题,但找不到在返回多个字段的联接表上使用基于行的条件的问题。无论如何,我使用它来填写ChildRev为NULL
的记录,但其中不包含ECO
列
UPDATE T
SET [ChildRev] = (SELECT TOP 1 I.[Rev] AS [ChildRev]
FROM [Items] AS I
WHERE (I.[Item] = T.[Child]
AND I.[RDate] <= T.[VDate])
ORDER BY I.[RDate] DESC
)
FROM [Tree] AS T
WHERE T.[ChildRev] IS NULL
这就是我得到的:
Parent | ParentRev | Child | ChildRev | VDate | ECO
-------+-----------+-------+----------+------------+------
Y | 0B | C | 01 | 2019-01-01 | NULL
Y | 0C | D | NULL | 2019-01-13 | NULL
Z | 01 | A | 01 | 2018-06-25 | NULL
Z | 02 | A | 0A | 2019-01-11 | NULL
Z | 0A | B | 0A | 2019-01-01 | NULL
我正在处理Tree表中的4.5M +条记录和Item表中的1.2M +条记录,并且每天都在增长。我有两个问题:
是否有更好(更快)的方式来更新Tree
表? (如果包含ECO
,则奖励)
当我添加新的Items
时,它们在1
字段中标记为New
(可能使用触发器)
如何使用新的Tree
Items
表
请注意,我无法真正控制数据的加载顺序(表格或日期)。
因此,显然Select first row in each GROUP BY group?基本上是解决方案,我只是没有意识到。专门介绍如何使用CTE更新我的数据表。感谢@Xedni给我的启发;我只真正使用CTE进行递归查询。因此,我最终得到了2个相似的CTE,
当我向Tree
表中添加新记录时,我添加了AND ChildRev IS NULL
以限制更新:
WITH CTE AS
(
SELECT ...
)
UPDATE CTE
SET ChildRev = ItemRev
WHERE RID = 1
AND ChildRev IS NULL
当我向Materials
表中添加新记录时,我添加了WHERE...ANY
子句:
WITH CTE AS
(
SELECT
...
RID = ROW_NUMBER() OVER (PARTITION BY t.Parent, t.ParentRev, t.Child
ORDER BY i.RDate DESC)
FROM #Tree t
JOIN #Items i
ON t.Child = i.Item
AND i.RDate <= t.VDate
WHERE I.Process = ANY (SELECT Item FROM #Items WHERE New = 1)
)
UPDATE CTE
SET ChildRev = ItemRev
WHERE RID = 1
答案 0 :(得分:1)
您可以通过联接获取所需的值,而不用在UPDATE
子句中使用相关子查询。首先,创建一个看起来与相关子查询几乎相同的派生表,并获取需要用来标识#Items
中要与#Tree
中的行关联的所有唯一值。由于没有任何迹象表明所提到的表具有唯一性约束,因此我不得不对此进行猜测。
设置示例数据
-- Setting up sample data
if object_id('tempdb.dbo.#Items') is not null drop table #Items
create table #Items
(
Item char(1),
Rev char(2),
RDate date,
ECO char(4),
New bit
)
insert into #Items (Item, Rev, RDate, ECO, New)
values
('A', '0A', '2019-01-01', 'E123', 1),
('A', '01', '2018-01-01', 'E456', 0),
('B', '0A', '2018-12-31', 'E765', 0),
('C', '01', '2019-01-01', 'E456', 0)
if object_id('tempdb.dbo.#Tree') is not null drop table #Tree
create table #Tree
(
Parent char(1),
ParentRev char(2),
Child char(1),
ChildRev char(2),
VDate date,
ECO char(4)
)
insert into #Tree (Parent, ParentRev, Child, ChildRev, VDate)
values
('Y', '0B', 'C', NULL, '2019-01-01'),
('Y', '0C', 'D', NULL, '2019-01-13'),
('Z', '01', 'A', NULL, '2018-06-25'),
('Z', '02', 'A', NULL, '2019-01-11'),
('Z', '0A', 'B', NULL, '2019-01-01')
现在,您有了派生表,将#tree
中的行映射到具有您希望从#items
开始的日期的行,再次将其联接到#items
表中以获得{{ 1}},ECO
以及您想要的其他任何内容。
Rev
通常来说,这可能会比相关子查询的性能更好,尽管根据所存在的索引,您的里程可能有所不同。另外,如果您确实要遍历450万条这样的记录,请考虑将其分成几批,或者想出一种方法来预过滤需要提前更新的内容。
关于启动新行时启动此过程,您有两个选择。
-- Actual Update Statement
update a
set ChildRev = c.Rev,
Eco = c.Eco
from #Tree a
-- Consruct a derived table basically mapping the rows in #tree to the rows with the desired dates you want.
inner join
(
select t.Child, t.ParentRev, MaxRDate = max(i.RDate)
from #Tree t
inner join #Items i
on t.Child = i.Item
and i.RDate <= t.VDate
group by t.Child, t.ParentRev
) b
on a.Child = b.Child
and a.ParentRev = b.ParentRev
-- Finally, join the "intermidate mapping table" to #Items to get the values (eco, rev, etc.) you actually want
inner join #Items c
on b.Child = c.Item
and b.MaxRDate = c.RDate
select top 1000 *
from #Tree
标志的数据的过程中,都要使它同时启动此过程(或类似的操作在同一事务中同时进行)。new
表上使用触发器执行相同的操作,并根据需要启动此过程。尽管TBH我还是建议使用TBH,因为它更容易在同一位置包含您需要的所有逻辑,并且没有触发器的额外开销,这在某种程度上也混淆了保持数据同步的过程。 li>
另一个选择
我刚想出的另一种方法是在单个查询中完成所有操作。使用带有Items
RID的CTE(或派生表;随心所欲)。然后在row_number
RID = 1