将“待处理数据更改”合并到视图中

时间:2018-04-10 16:01:17

标签: tsql

我有一个待处理更改表,我正在尝试将更改应用到视图中,以便查看最新数据。

首先是一些背景:我有一个网站,允许对Project Web Access(PWA)中的数据进行简单的更改。 PWA的一个问题是您无法直接在其数据库中更改数据,因此我使用SharePoint的CSOM来检出,修改,签入,发布和保存对项目和任务的更改。这需要10-20秒,这对于网站上的用户体验来说太慢了。

我通过将修改后的字段写入待处理的更改表来解决这个问题,即行的标识符,字段已更改以及新值。我目前在c#.Net(RBAR)中合并这些更改,以便显示网站。

我想在SQL服务器中合并这些数据,最好不要逐行合并。

我正在这里测试

WITH
SourceDataView (id, name, age, joined) AS 
(
    SELECT 1, 'John' , 45, Cast('2018-01-15' as DateTime) UNION 
    SELECT 2, 'Paul', 33, Cast('2018-02-08' as DateTime) UNION 
    SELECT 3, 'George', 39, Cast('2018-03-29' as DateTime)
), 
PendingChanges (id, foreignId, fieldName, fieldValue) AS 
(
    SELECT 1, 2, 'name', 'Peter' UNION 
    SELECT 2, 1, 'age', '34' UNION 
    SELECT 3, 3, 'joined', '2018-02-22' UNION 
    SELECT 4, 1, 'joined', '2018-01-10' UNION 
    SELECT 5, 3, 'joined', '2017-12-30'
)

-- Just show the joined data
SELECT * FROM 
    SourceDataView s
        LEFT JOIN
    PendingChanges p
        on s.id = p.foreignId
ORDER BY p.id;

-- This is what I am expecting
WITH
DesiredResultsView (id, name, age, joined) AS (
    SELECT 1, 'John' , 34, Cast('2018-01-10' as DateTime) UNION 
    SELECT 2, 'Peter', 33, Cast('2018-02-08' as DateTime) UNION 
    SELECT 3, 'George', 39, Cast('2017-12-30' as DateTime)
) SELECT * from DesiredResultsView

如果数据在上次编辑中获得两次更改,则需要注意几点。此外,数据类型需要正确转换。

2 个答案:

答案 0 :(得分:0)

而不是尝试使用PendingChanges格式的SourceDataView Work格式的数据:

要首先对源进行UNPIVOT,然后您将能够将更改与简单的UNION合并。然后对每行的更改进行编号,并仅保留每个字段的最新版本。最后将数据PIVOT回到所需的形状并重新应用原始数据类型。

WITH
SourceDataView (id, name, age, joined) AS 
(
    SELECT 1, 'John' , 45, Cast('2018-01-15' as DateTime) UNION 
    SELECT 2, 'Paul', 33, Cast('2018-02-08' as DateTime) UNION 
    SELECT 3, 'George', 39, Cast('2018-03-29' as DateTime)
), 
PendingChanges (id, foreignId, fieldName, fieldValue) AS 
(
    SELECT 1, 2, 'name', 'Peter' UNION 
    SELECT 2, 1, 'age', '34' UNION 
    SELECT 3, 3, 'joined', '2018-02-22' UNION 
    SELECT 4, 1, 'joined', '2018-01-10' UNION 
    SELECT 5, 3, 'joined', '2017-12-30'
),

SourceUnpivot (id, foreignId, fieldName, fieldValue) AS 
(
    SELECT 0 as id, id as foreignId, fieldName, fieldValue
    FROM 
       (SELECT id
            , cast(name as sql_variant) name
            , cast(age as sql_variant) age
            , cast(joined as sql_variant) joined
       FROM SourceDataView) p
    UNPIVOT
       (fieldValue FOR fieldName IN 
          (name, age, joined)
    ) AS _SourceUnpivot
),
MergeChanges as
(
    SELECT * FROM SourceUnpivot
    UNION SELECT * from PendingChanges
),
NumberedChanges as 
(
    SELECT *, 
        ROW_NUMBER() OVER(PARTITION BY foreignID, fieldName ORDER BY id DESC) as ChangeNumber
    from MergeChanges
),
MergePivot as 
(
    SELECT id,
        Cast(name as nvarchar(max)) as name, 
        Cast(age as int) as age, 
        Cast(joined as DateTime) as joined
    FROM (
        SELECT foreignId as id, fieldName, fieldValue
        from NumberedChanges
        where ChangeNumber = 1
    ) as LastChangeOnly
    PIVOT (
        MAX(fieldValue)
        FOR fieldName in (name, age, joined)
    )
    as _MergePivot
)

SELECT * from MergePivot ORDER BY id;

WITH
DesiredResultsView (id, name, age, joined) AS (
    SELECT 1, 'John' , 34, Cast('2018-01-10' as DateTime) UNION 
    SELECT 2, 'Peter', 33, Cast('2018-02-08' as DateTime) UNION 
    SELECT 3, 'George', 39, Cast('2017-12-30' as DateTime)
) SELECT * from DesiredResultsView

答案 1 :(得分:0)

我认为你可能会发现这个解决方案更简单一点,希望性能也更好一些。

CREATE TABLE SourceDataView
(
    ID INT
    ,[Name] VARCHAR(50)
    ,Age TINYINT
    ,Joined DateTime
)
;

CREATE TABLE PendingChanges
(
    ID INT
    ,ForeignID INT
    ,FieldName VARCHAR(10)
    ,FieldValue VARCHAR(100)
)
;

INSERT INTO SourceDataView
 VALUES
 (1, 'John' , 45, '2018-01-15')  
 ,(2, 'Paul', 33, '2018-02-08')
 ,(3, 'George', 39, '2018-03-29')
 ;

 INSERT INTO PendingChanges
 VALUES
(1, 2, 'name', 'Peter') 
,(2, 1, 'age', '34' )
,(3, 3, 'joined', '2018-02-22') 
,(4, 1, 'joined', '2018-01-10')
,(5, 3, 'joined', '2017-12-30')
;

WITH CTE AS
(
    SELECT *
    FROM 
    (
        SELECT
            ROW_NUMBER() OVER (PARTITION BY ForeignID,FieldName ORDER BY ID DESC) AS ChangeNumber
            ,*
        FROM PendingChanges
    ) AS RowNum
    PIVOT
    (
        MAX(FieldValue)
        FOR FieldName IN ([name],[age],[joined])
    ) AS P 
)
SELECT
    SDV.ID
    ,COALESCE(CA.[name],SDV.[name]) AS [name]
    ,COALESCE(CA.age,SDV.age) AS age
    ,COALESCE(CA.joined,SDV.joined) AS joined
FROM SourceDataView AS SDV
CROSS APPLY
(
    SELECT
        (SELECT [name] FROM CTE WHERE CTE.ForeignID = SDV.ID AND CTE.ChangeNumber = 1 AND CTE.[name] IS NOT NULL) AS [name]
        ,(SELECT age FROM CTE WHERE CTE.ForeignID = SDV.ID AND CTE.ChangeNumber = 1 AND CTE.age IS NOT NULL) AS age
        ,(SELECT joined FROM CTE WHERE CTE.ForeignID = SDV.ID AND CTE.ChangeNumber = 1 AND CTE.joined IS NOT NULL) AS joined
) AS CA