我有下表:
DECLARE @TABLE_A TABLE (
id int identity,
name varchar(20),
start_date datetime,
end_date datetime,
details nvarchar(500),
copied_from int)
用户可以克隆一行并将其重新插入到同一个表中,我们会记录它从哪一行复制。因此,如果您有一个ID = 1的行并且您复制了所有列并重新插入(从UI),您将获得ID = 5的新行,而新行的copied_from字段的值将为1。
此用户可以更新新的行值(本例中为ID 5),我们需要一种方法来查看2行之间的差异。我已经写了下面的内容来获取ID 1和ID 5之间的差异。
DECLARE @id int = 5
DECLARE @TABLE_A TABLE (id int identity, name varchar(20), start_date datetime, end_date datetime, details nvarchar(500), copied_from int)
INSERT INTO @TABLE_A (name, start_date, end_date, details, copied_from)
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'John', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up - changed</p>', 1
SELECT
'Name' AS column_name,
ISNULL(s.name, '') AS value_before,
ISNULL(t.name, '') AS value_after,
t.id,
t.copied_from
FROM @TABLE_A s
FULL OUTER JOIN @TABLE_A t ON s.id = t.copied_from
WHERE t.id = @id AND ISNULL(s.name, '') <> ISNULL(t.name, '')
UNION ALL
SELECT
'Details' AS column_name,
ISNULL(s.details, '') AS value_before,
ISNULL(t.details, '') AS value_after,
t.id,
t.copied_from
FROM @TABLE_A s
FULL OUTER JOIN @TABLE_A t ON s.id = t.copied_from
WHERE t.id = @id AND ISNULL(s.details, '') <> ISNULL(t.details, '')
.......
正如您所看到的,ID和COPIED_FROM字段中存在自联接,并且对于每个列,我检查是否存在差异。
这有效,但不知何故我对每列的重复UNIONS感到不满意,我想知道是否还有其他方法可以达到这个目的?
由于
答案 0 :(得分:1)
尝试以下脚本,这可能会对您有所帮助。使用CASE WHEN
表达式,我们可以识别修改的列。但是这将只返回包含所有细节的单个记录(在值之前,在值和状态之后 - 1:修改/ 0:不是)。
SELECT ISNULL(s.name, '') AS name_before,
ISNULL(t.name, '') AS name_after,
(case when s.name <> t.name then 1 else 0 end) AS name_status,
ISNULL(s.details, '') AS details_before,
ISNULL(t.details, '') AS details_after,
(case when s.details <> t.details then 1 else 0 end) AS details_status
FROM @TABLE_A s
INNER JOIN @TABLE_A t ON s.id = t.copied_from
WHERE t.id = @id
答案 1 :(得分:1)
你可以使用动态脚本,即使表有数百列,也没关系。
CREATE TABLE #tt (id int identity, name varchar(20), start_date datetime, end_date datetime, details nvarchar(500), copied_from int)
INSERT INTO #tt (name, start_date, end_date, details, copied_from)
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'John', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up - changed</p>', 1
DECLARE @cols VARCHAR(max),@sql VARCHAR(max)
SELECT @cols=ISNULL(@cols+',('''+c.Name+''',CONVERT(VARCHAR,o.[','('''+c.Name+''',CONVERT(VARCHAR,o.[')+c.name+']),CONVERT(VARCHAR,c.['+c.name+']))'
FROM tempdb.sys.columns AS c WHERE c.object_id=OBJECT_ID('tempdb..#tt')
PRINT @cols
SET @sql='
SELECT x.* FROM #tt AS c
LEFT JOIN #tt AS o ON c.copied_from=o.id
CROSS APPLY(values'+@cols+') AS x(columnName,OrignalValue,CopiedValue)
WHERE c.copied_from IS NOT NULL'
PRINT @sql
EXEC(@sql)
columnName OrignalValue CopiedValue ----------- ------------------------------ ------------------------------ id 1 5 name Tom John start_date Jan 1 2017 12:00AM Jan 1 2017 12:00AM end_date Feb 1 2017 12:00AM Feb 1 2017 12:00AM detailsthis column can contain htm
this column can contain htm copied_from NULL 1
答案 2 :(得分:1)
好吧,我想最初的要求是将所有更改聚合到一个集合中(ColumnName,before,after,id,copied_from)。
我想您可能希望使用unpivot,例如:
DECLARE @id int = 5
DECLARE @TABLE_A TABLE (id int identity, name varchar(20), start_date datetime, end_date datetime, details nvarchar(500), copied_from int)
INSERT INTO @TABLE_A (name, start_date, end_date, details, copied_from)
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'Tom', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up</p>', null UNION ALL
SELECT 'John', '2017-01-01', '2017-02-01', '<p>this column can contain html mark up - changed</p>', 1 ;
WITH t AS ( SELECT id,
CAST(ISNULL(name, '') AS NVARCHAR(500)) name,
CAST(start_date AS NVARCHAR(500)) start_date,
CAST(end_date AS NVARCHAR(500)) end_date,
details,
copied_from
FROM @TABLE_A
),
m AS ( SELECT u.id,
u.copied_from,
u.column_name,
u.data
FROM t UNPIVOT( data FOR column_name IN ( name, start_date,
end_date, details ) ) u
)
SELECT toT.column_name,
fromT.data value_before,
toT.data value_after,
toT.id,
toT.copied_from
FROM m fromT
INNER JOIN m toT ON toT.copied_from = fromT.id AND
toT.column_name = fromT.column_name AND
toT.data <> fromT.data;
注意:我必须将所有字段强制转换为nvarchar(对于需要取消忽略的所有列都是一致的),否则UNPIVOT将无效...
答案 3 :(得分:0)
您也可以通过使用INNER JOIN
来实现这一点,但我想说这将是以单行显示更改值并且更具可读性的正确方法
select s.name AS [name_before],
t.name AS [name_after],
s.details AS [detail_before],
t.details AS [detail_after],
t.id,
s.id AS [copied_from]
FROM @TABLE_A s
INNER JOIN @TABLE_A t ON s.id = t.copied_from
WHERE t.id = @id
AND (s.name <> t.name OR s.details <> t.details)
我们可以使用OR
我们必须显示两者或一个来自名称&amp;细节是变化。