有2个表,一个Component表和一个Log表。组件表包含实际(当前)值描述以及上次更新时的时间戳。
Log表包含一个引用它所属的组件的组件ID:
Component:
Id
Actual
LastUpdated
Log:
Id
ComponentId
Value
Timestamp
以前工作但当前锁定表的查询看起来像这样。
update Component set Actual =
(select top 1 Value from Log
where Component.Id = ComponentId
order by Id desc),
LastUpdated =
(select top 1 TimeStamp from Log
where Component.Id = ComponentId
order by Id desc)
日志和组件表都在增长,而且这个查询无法像过去那样保持同步。现在有大约80个组件和几百万条记录。
是否可以以这样的方式工作并且只是改进查询或者整个方法是错误的?
ps发送数据的设备没有可靠的系统时间,因此更新组件表会导致不一致。插入日志时,我需要在SQL服务器上获取系统时间(默认值)
编辑:
从awnsers中获取建议我尝试在日志上创建触发器,以便在创建日志时自动更新Component。
CREATE TRIGGER trg_log_ins
ON Log
AFTER INSERT
AS
BEGIN
update Component
SET Actual = (SELECT i.value FROM inserted as i),
LastUpdated = (SELECT i.Timestamp FROM inserted as i);
END;
但由于某种原因,查询未完成并继续执行。
答案 0 :(得分:1)
我认为你这一切都错了。更好的解决方案是Component表上的触发器,只要插入或更新Component,就会插入到Log表中。
CREATE TRIGGER trg_component_biu
ON Component
AFTER INSERT, UPDATE
AS
BEGIN
INSERT INTO Log(
ComponentId,
Value,
Timestamp
)
SELECT
Id,
Actual,
LastUpdated
FROM inserted;
END;
答案 1 :(得分:0)
根据您的查询中的TOP 1
,我猜您使用的是SQL SERVER
。在SQL Server中,您可以使用OUTER APPLY
UPDATE c
SET c.Actual = cs.Value,
c.LastUpdated = cs.TimeStamp
FROM Component C
OUTER apply (SELECT TOP 1 TimeStamp,
ComponentId
FROM Log l
WHERE c.Id = l.ComponentId
ORDER BY Id DESC) cs
在non-clustered index
表Log
列上添加ID
并添加TimeStamp,ComponentId
可提高查询效果
另一种方法是使用ROW_NUMBER
和LEFT OUTER JOIN
UPDATE c
SET c.Actual = cs.Value,
c.LastUpdated = cs.TimeStamp
FROM Component C
LEFT OUTER JOIN (SELECT Row_number()OVER(partition BY ComponentId
ORDER BY id DESC) rn,*
FROM Log) cs
ON cs.ComponentId = c.id
AND cs.rn = 1
答案 2 :(得分:0)
你可以像这样使用ROW_NUMBER():
UPDATE t1
SET t1.Actual = t2.value,
t1.LastUpdated = t2.TimeStamp
FROM Component t1
INNER JOIN (SELECT log.*,ROW_NUMBER() OVER (PARTITION BY log.componentID order by log.ID DESC) as rnk
FROM log) t2
ON(t2.componentID = t1.id and t2.rnk = 1)
答案 3 :(得分:0)
Component表中的所有数据都来自Log表。您可以将其设为视图,并在必要时编制索引,而不是将Component设为实际表。
CREATE VIEW Component
WITH SCHEMABINDING
AS
SELECT
ComponentId AS Id,
FIRST_VALUE(Value)
OVER(PARTITION BY ComponentId
ORDER BY Timestamp DESC)
AS Actual,
MAX(Timestamp) AS LastUpdated
FROM Log
GROUP BY ComponentId;
答案 4 :(得分:0)
如果要在Log
表上使用触发器,即使插入了多行,它也必须工作。这是一种可能的变体。
此外,此变体不会捕获ComponentID
表中尚不存在的新Component
的值。
如果有可能将此类值插入Log
表,我会使用MERGE
而非简单UPDATE
。
CREATE TRIGGER trg_log_ins
ON Log
AFTER INSERT
AS
BEGIN
WITH
CTE
AS
(
SELECT
Component.Actual AS OldValue
,Component.LastUpdated AS OldTimestamp
,inserted.Value AS NewValue
,inserted.Timestamp AS NewTimestamp
FROM
Component
INNER JOIN inserted ON inserted.ComponentID = Component.ID
)
UPDATE CTE
SET
OldValue = NewValue,
OldTimestamp = NewTimestamp
;
END
此外,如果可以在同一Log
语句中使用相同的ComponentID
插入INSERT
多个行,您最好明确选择要用于哪个值更新。可能是最新的Timestamp
。
因此,查询变得更加复杂:
CREATE TRIGGER trg_log_ins
ON Log
AFTER INSERT
AS
BEGIN
WITH
CTE_InsertedRowNumbers
AS
(
SELECT
inserted.ComponentID
,inserted.Value AS NewValue
,inserted.Timestamp AS NewTimestamp
,ROW_NUMBER() OVER (
PARTITION BY inserted.ComponentID
ORDER BY inserted.Timestamp DESC, inserted.ID DESC) AS rn
FROM inserted
)
,CTE_LatestInsertedComponents
AS
(
SELECT
ComponentID
,NewValue
,NewTimestamp
FROM CTE_InsertedRowNumbers
WHERE rn = 1
)
,CTE
AS
(
SELECT
Component.Actual AS OldValue
,Component.LastUpdated AS OldTimestamp
,CTE_LatestInsertedComponents.NewValue
,CTE_LatestInsertedComponents.NewTimestamp
FROM
Component
INNER JOIN CTE_LatestInsertedComponents
ON CTE_LatestInsertedComponents.ComponentID = Component.ID
)
UPDATE CTE
SET
OldValue = NewValue,
OldTimestamp = NewTimestamp
;
END