寻找此查询的替代方法

时间:2016-03-03 08:12:08

标签: sql sql-server

有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;

但由于某种原因,查询未完成并继续执行。

5 个答案:

答案 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 indexLog列上添加ID并添加TimeStamp,ComponentId可提高查询效果

另一种方法是使用ROW_NUMBERLEFT 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