仅在SQL表

时间:2017-04-05 11:46:59

标签: sql-server sql-server-2008

在这个项目中,每个表都有一个“历史表”,它与原始表相同,但添加了以下列:[HistId],[ActionUser],[ActionCode],[ActionDate],[ValidUntil] 基本上在每次插入,更新,删除时,我们都会像日志一样。之后,我们很容易看到用户做了什么,谁做了什么,什么时候做了。

看看它的外观,here

问题在于数据历史记录的显示方式。我看到它基本上是全部选择,所有内容都像excel电子表格一样显示。 很难找到改变的东西和不改变的东西。大多数情况下,更改一次只能进行一列。当你试图找到具体的东西时,很难看到你需要的信息。

我在考虑使用不同的方法。 我想只显示看起来像日志的内容中的内容。 (我也会在以后搜索这个)

它的样子:

Record with title "a" inserted by albert on 2010-01-01

Record's Title was updated from "a" to "b" by john on 2010-01-02

Record's Title was updated from "b" to "c" by dave on 2010-01-03

Record's Description was updated from "abc" to "def" by paul on 2010-01-04

...etc...

Record was deleted by bin on 2010-01-08

对于6日的变化(标题和描述),我想要有1条记录,无论是2条记录,哪条都不那么复杂。 所以基本上我正在寻找一种方法,从一组记录中显示只有变化的数据,没有别的

额外信息:

  1. 历史表的结构无法更改,因为有很多这样的表。

  2. 我只想改变检索数据的方式。

  3. 有20列以上的表格

  4. 有些表有3000万条记录,所以性能很重要,但通常不会显示太多数据,我会说最多50条记录)

  5. 在每一行都有“from”和“to”,看起来不错,但如果它太复杂,那么“to”就够了

2 个答案:

答案 0 :(得分:1)

假设 i)无论出于何种原因,历史表都不会发生变化。

ii)因为你一次会显示50,100,150条记录。所以百万条记录不是问题。

试试这个让我知道,

create table #side ([Id] [int] IDENTITY NOT NULL,
                [Title] [varchar](50) NOT NULL,
                [Description] [varchar](250) NULL)

create table #h ([HistId] [int] IDENTITY(1,1) NOT NULL,
                [Id] [int] NOT NULL,
                [Title] [varchar](50) NOT NULL,
                [Description] [varchar](250) NULL,
                [TypeId] [int] NULL,
                [ActionUser] [int] NULL,
                [ActionCode] [char](1) NOT NULL,
                [ActionDate] [datetime] NOT NULL,
                [ValidUntil] [datetime] NULL)

insert into #side ([Title],[Description]) values ('a','abc')

insert into #h ([Id],[Title],[Description],[TypeId],[ActionUser],[ActionCode],[ActionDate],[ValidUntil]) values (1,'a','abc',123,991,'i','01/01/2010',NULL) 

declare @mod datetime;
set @mod = '01/02/2010'

insert into #h ([Id],[Title],[Description],[TypeId],[ActionUser],[ActionCode],[ActionDate],[ValidUntil]) values (1,'b','abc',123,991,'u',@mod,NULL) 


insert into #h ([Id],[Title],[Description],[TypeId],[ActionUser],[ActionCode],[ActionDate],[ValidUntil]) values (1,'c','abc',123,991,'u',@mod,NULL) 

insert into #h ([Id],[Title],[Description],[TypeId],[ActionUser],[ActionCode],[ActionDate],[ValidUntil]) values (1,'c','def',123,991,'u',@mod,NULL) 

insert into #h ([Id],[Title],[Description],[TypeId],[ActionUser],[ActionCode],[ActionDate],[ValidUntil]) values (1,'d','pqr',123,991,'u',@mod,NULL) 
select * from #h order by HistId Desc
--select * from #side

select h.HistId, case when h.ActionCode='i' THEN
'Record with Title "'+h.title+'" inserted '
when h.ActionCode='u' THEN
'Records '+ case 
when h.title<>h1.title and h.[Description]<>h1.[Description]  then  'Title,Description was updated from 
"'+h1.title+'","'+h1.[Description]+'" to " '+h.title+' "  , "'+h.[Description]+'" respectively'
when h.title<>h1.title then  'Title was updated from "'+h1.title+'" to " '+h.title+' " '
when h.[Description]<>h1.[Description] then  'Description was updated from "'+h1.[Description]+'" to " '+h.[Description]+' " '
 else '' end
 when h.ActionCode='d' THEN
 'Record was deleted'
 else
null
END
+'by '+cast(h.ActionUser as varchar)+' on '+convert(varchar(10),h.actiondate ,120)+''
from #h h
left join #h h1
on h.HistId=(h1.HistId+1)
--left join #usertable u
--on u.userid=h.[ActionUser]

drop table #h
drop table #side

答案 1 :(得分:0)

这是一种简单的方法,它使用self-join将每条记录与之前的条目进行比较。该技术假设HistId是一个完整的整数序列。

示例数据

/* Sample data.
 * Three records are created.   
 * The first is a base line.  
 * The next two are updates.
 * Update one contains one change.
 * Update two contains two changes.
 *
 * See also: http://meta.stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-me-to-be-a-very-simple-sql-query
 */ 
DECLARE @SampleHistory TABLE 
(
    HistoryId       INT IDENTITY(1, 1),
    Title           VARCHAR(255),
    [Description]   VARCHAR(255)
)


INSERT INTO @SampleHistory
(
    Title,
    [Description]
)
VALUES  
    ('Data Warehouse Toolkit', 'First Edition'),            -- Base record.
    ('Data Warehouse Toolkit', 'Second Edition'),           -- Description changed.
    ('The Data Warehouse Toolkit', 'Third Edition')         -- Name and description changed.
;

<强>查询

/* Change detection, using self join.
 * See also:  https://msdn.microsoft.com/en-us/library/ms177490.aspx
 */
SELECT
    CASE
        WHEN h2.Title IS NULL THEN 'New Title: ' + h1.Title + '.  '
        WHEN h1.Title <> h2.Title THEN 'Title updated from ' + h2.Title + ' to ' + h1.Title + '.  '
        ELSE ''
    END
    +
    CASE
        WHEN h2.[Description] IS NULL THEN 'New Description: ' + h1.[Description] + '.  '
        WHEN h1.[Description] <> h2.Title THEN 'Description updated from ' + h2.[Description] + ' to ' + h1.[Description] + '.  '
        ELSE ''
    END
FROM
    @SampleHistory AS h1
        LEFT OUTER JOIN @SampleHistory AS h2            ON h2.HistoryId = (h1.HistoryId - 1)
;

这种方法的缺点是你必须为每个历史表中的每个字段编写一个case表达式。不好。避免所有这种输入的一种方法是使用dynamic SQL。但这可能很复杂!

最后的想法

我不认为SQL非常适合这样的任务。由于其基于集合的性质,循环字段和有条件地返回值需要花费很多精力。在C#,Java等中,你可以用更少的努力获得相同的结果。

对于所有版本,SQL Server 2016包含Change Data Capture开箱即用。 CDC包含在SQL Server 2008,2012和2014 Enterprise和Developer版本中。在可能的情况;我更喜欢使用本机功能。手动解决方案需要管理,并且通常缺乏开发人员添加的高级功能。