确定哪些列已在表的行中更改

时间:2013-04-19 01:56:48

标签: sql sql-server tsql

我有一个SQL Server表,其中包含与各种Measurement s相关的几个不同Attribute的增量Object

MeasurementID  ObjectID    Attribute1  Attribute2  Attribute3
100            1           'blue'      111         'large'
101            1           'blue'      114         'large'
102            1           'red'       114         'large'
103            1           'red'       117         'large'
104            1           'cyan'      118         'large'
105            2           'blue'      450         'huge'
106            3           'blue'      450         'huge'
107            3           'red'       450         'huge'

我的目标是找到一个有效的查询,给定一个特定的ObjectID告诉我哪些属性发生了变化。例如,让我们说ObjectID=1Attribute1 MeasurementID=102(从'blue'更改为'red'Attribute2更改MeasurementID=101 MeasurementID=103Attribute3更改为NULL CREATE FUNCTION GetMeasurementChanges (ObjectID int, AttributeName varchar) RETURNS @returnMeasurementIDs TABLE (MeasurementID int) -- rows of MeasurementIDs 时间ObjectObject。从概念上讲,我正在寻找符合这种伪代码定义的东西:

Attribute

我可以通过在{{1}} - by - {{1}}基础上选择临时表然后遍历行,测试每个{{1但是我怀疑它的表现非常糟糕。

任何人都可以指点我的技巧或类似问题吗?

谢谢!

4 个答案:

答案 0 :(得分:3)

试试这个

if (@attributeName = 'Attribute1')
begin
  insert into @returnMeasurementIDs
  select measurementid from (
  select measurementid,
  rank() over (partition by objectId, attribute1 order by measurementid) rnk,
  row_number() over (order by measurementid) rn
  from table where objectid = @ObjectID
  ) v where rnk = 1 and rn <> 1
end
else if (@attributeName = 'Attribute2')
begin
  insert into @returnMeasurementIDs
  select measurementid from (
  select measurementid,
  rank() over (partition by objectId, attribute2 order by measurementid) rnk,
  row_number() over (order by measurementid) rn
  from table where objectid = @ObjectID
  ) v where rnk = 1 and rn <> 1
end
else if (@attributeName = 'Attribute3')
begin
  insert into @returnMeasurementIDs
  select measurementid from (
  select measurementid,
  rank() over (partition by objectId, attribute3 order by measurementid) rnk,
  row_number() over (order by measurementid) rn
  from table where objectid = @ObjectID
  ) v where rnk = 1 and rn <> 1
end

SQL DEMO

Rank - 用于按objectid和属性对每个组进行排名,当属性发生变化时,它将返回1。

Row_Number用于对所有行进行排名,并忽略所有已更改的测量ID列表中的第一行,以便返回行102,104等。

答案 1 :(得分:1)

CREATE FUNCTION dbo.GetMeasurementChanges -- always use schema prefix
(
  @ObjectID INT, @AttributeName VARCHAR(32)
)
RETURNS TABLE -- use an inline table-valued function when possible
WITH SCHEMABINDING
AS
  RETURN 
  (
    WITH x AS 
    (
      SELECT ObjectID, MeasurementID, Attribute1, Attribute2, Attribute3, 
        rn = ROW_NUMBER() OVER (ORDER BY MeasurementID)
      FROM dbo.MyTable -- update this of course
      WHERE ObjectID = @ObjectID
    ), y AS 
    (
      SELECT MeasurementID,
        r1 = CASE @AttributeName WHEN 'Attribute1' THEN ROW_NUMBER() 
             OVER (PARTITION BY Attribute1 ORDER BY MeasurementID) END,
        r2 = CASE @AttributeName WHEN 'Attribute2' THEN ROW_NUMBER() 
             OVER (PARTITION BY Attribute2 ORDER BY MeasurementID) END,
        r3 = CASE @AttributeName WHEN 'Attribute3' THEN ROW_NUMBER() 
             OVER (PARTITION BY Attribute3 ORDER BY MeasurementID) END
      FROM x
    )
    SELECT MeasurementID FROM y WHERE 1 IN (r1, r2, r3)
      AND NOT EXISTS 
      (
        SELECT 1 FROM x WHERE x.rn = 1 
        AND MeasurementID = y.MeasurementID
      )
  );
GO

替代方案:

CREATE FUNCTION dbo.GetMeasurementChanges -- always use schema prefix
(
  @ObjectID INT, 
  @AttributeName VARCHAR(32)
)
RETURNS TABLE -- use an inline table-valued function when possible
WITH SCHEMABINDING
AS
  RETURN 
  (
    WITH x AS 
    (
      SELECT ObjectID, MeasurementID, Attribute1, Attribute2, Attribute3, 
        rn = ROW_NUMBER() OVER (ORDER BY MeasurementID)
      FROM dbo.MyTable WHERE ObjectID = @ObjectID
    ),
    y AS 
    (
      SELECT MeasurementID, r = ROW_NUMBER() OVER (
            PARTITION BY CASE @AttributeName 
          WHEN 'Attribute1' THEN Attribute1
          WHEN 'Attribute2' THEN CONVERT(VARCHAR(32), Attribute2)
          WHEN 'Attribute3' THEN Attribute3 END 
        ORDER BY MeasurementID)
      FROM x
    )
    SELECT MeasurementID FROM y WHERE r = 1
      AND NOT EXISTS 
      (
        SELECT 1 FROM x WHERE x.rn = 1 
        AND MeasurementID = y.MeasurementID
      )
  );
GO

答案 2 :(得分:0)

试试这个

SELECT  CASE WHEN a.Attribute1 = b.Attribute1 THEN 1 END AS Attribute1,
    CASE WHEN a.Attribute2 = b.Attribute2 THEN 1 END AS Attribute2,
    CASE WHEN a.Attribute3 = b.Attribute3 THEN 1 END AS Attribute3
FROM    Measurements a
    INNER JOIN Measurements b
    ON (b.MeasurementID = (SELECT MAX(MeasurementID) FROM Measurements c WHERE    c.MeasurementID < a.MeasurementID AND c.ObjectID = a.ObjectID))

答案 3 :(得分:0)

使用选项TOP 1 + CROSS APPLY运算符

CREATE FUNCTION dbo.getMeasurementChanges(@ObjectID int, @AttributeName nvarchar(10))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
 (
  SELECT o.MeasurementID
  FROM dbo.test90 t 
    CROSS APPLY (
                 SELECT TOP 1 MeasurementID, ObjectID,
                   CASE @AttributeName WHEN 'Attribute1' THEN t.Attribute1
                                       WHEN 'Attribute2' THEN CAST(t.Attribute2 AS nvarchar(10))
                                       WHEN 'Attribute3' THEN t.Attribute3 END AS t1Attributes,
                   CASE @AttributeName WHEN 'Attribute1' THEN t2.Attribute1
                                       WHEN 'Attribute2' THEN CAST(t2.Attribute2 AS nvarchar(10))
                                       WHEN 'Attribute3' THEN t2.Attribute3 END AS t2Attributes
                 FROM dbo.test90 t2
                 WHERE t2.ObjectID = @ObjectID AND t.MeasurementID < t2.MeasurementID                                      
                 ORDER BY MeasurementID ASC
                 ) o
WHERE t.ObjectID = @ObjectID AND t1Attributes != t2Attributes)

为了提高性能,请使用以下索引:

CREATE INDEX x ON dbo.test90(ObjectID) INCLUDE(MeasurementID, Attribute1, Attribute2, Attribute3)
CREATE INDEX x2 ON dbo.test90(MeasurementID) INCLUDE(ObjectID, Attribute1, Attribute2, Attribute3)

SQLFiddle上的演示