加拿大家园论坛

数据库大神请点进来,有道SQL面试题求帮助

原文链接:https://forum.iask.ca/threads/849208/

cetaphil : 2018-04-19#1
应届生求职,面试full stack developer,第二面不停地问数据库问题,本人是数据库小白,就会写写sql query什么的。请问有人知道这题怎么做吗?

大概意思就是说如何写一个sql object,使你在任何时间都可以exactly reproduce past results(因为数据库每时每刻都在改变和更新所以在不同时间抓出来的信息不一样)

总共只有两个table:

  1. Dbo.data1 (main data table, primary key is on date,id_security)
  2. Data audit.data1 (audit table that contains history of changes. Changedate is date-time of the changes)
  • 主table叫dbo.data1 ,有[date], [id_security], 和其他fields。
  • 辅table叫data_audit.data1,有[id_data_audit_data1], [date], [id_security], [ChangeDate], [ChangeUser], [ChangeAction] 和其他fields。
  • 每一次主table发生了变化,辅table就会记录下变化的时间,id_security,ChangeAction等等
面试题是:Create SQL objects to return Point-in-time data that generate past results
  1. Create SQL object to show data in the same structure as dbo.data1 but as of certain date/time.
To be more clear
  1. Now time is 2018-02-22 14:42:22.
    • I do SELECT * FROM dbo.data1 WHERE date = '2011-12-31'
    • and get some records.
  2. In the future, even though records can be updated or deleted, if
    • I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')
    • I will have to get identical output as I see now with the query under a.a (SELECT * FROM dbo.data1 WHERE date = '2011-12-31')
  3. Based on the code in triggers, I would like you to elaborate on:
    • Our approach to data audits. Triggers and PIT functions are generated automatically with stored procedure, so maintenance is not a problem.
    • Ideas for improvement. There is certainly room for improvement.
    • Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.
    • Any suggestions for audit table compression and/or partitioning.

    请各位大侠指点迷津,小女子感激不尽。
更新:面试官加了一条:FYI, our live data table have 17 million records and 8 times more in audit table. Therefore, additional indexing is important part of the solution.

附件


dennistan2009 : 2018-04-20#2
首先,看了一遍题目,我的感觉这是个开放性的问题,也就是说并不一定有统一标准答案。
其次,题目的大概意思是每一次通过时间戳抓取的都是最新更新的数据,排除掉以前变化的数据。
最后,找了一遍目录,没找到这个PIT functions are generated automatically,估计就把这个PIT function写一下就行了,
PIT function也很简单就是抓取point in time数据,就是根据时间戳到Audit表里fetch一些数据就可以了。
至于其他的improvement, solution, suggestion就根据你学过的数据库知识对照这个题目套用一下,稍微延伸一下就可以了。

cetaphil : 2018-04-20#3
首先,看了一遍题目,我的感觉这是个开放性的问题,也就是说并不一定有统一标准答案。
其次,题目的大概意思是每一次通过时间戳抓取的都是最新更新的数据,排除掉以前变化的数据。
最后,找了一遍目录,没找到这个PIT functions are generated automatically,估计就把这个PIT function写一下就行了,
PIT function也很简单就是抓取point in time数据,就是根据时间戳到Audit表里fetch一些数据就可以了。
至于其他的improvement, solution, suggestion就根据你学过的数据库知识对照这个题目套用一下,稍微延伸一下就可以了。

感谢回复!我的思路大概就是建一个table-valued user-defined function: dbo.data1_PIT(@data_date smalldatetime, @as_of_date datetime),return 一个data for one date。因为在校没上过数据库这门课还不太会写function。。还有就是对dbo.data1进行怎样的操作才会记录在data_audit.data1里面呢?是通过看trigger有哪几种吗?您觉得有哪些improvement/suggestion可取呢?

附件


kirkli : 2018-04-20#4
用triggers来写,你的题目第三条已经要求了, 网上找一下怎么写这属于t-sql

Creat trigger xxx on xxx
After insert, update, delete
As
If .....
Insert into xxx1 ()

cetaphil : 2018-04-20#5
用triggers来写,你的题目第三条已经要求了, 网上找一下怎么写这属于t-sql

Creat trigger xxx on xxx
After insert, update, delete
As
If .....
Insert into xxx1 ()
大神好,trigger已经写好了,分别是delete,update和insert,现在的问题就是怎么写这个function dbo.data1_PIT,完全没写过function不知道怎么下手。。
代码:
ALTER TRIGGER [dbo].[trg_data1_Delete_all_fields]

    ON [dbo].[data1]

FOR DELETE

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()
  
    -- [ChangeAction]=D means delete
    INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'D' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted

fieldmarshal : 2018-04-20#6
你把头像换个美女照马上一群人给你把答案做好

zhizhi123 : 2018-04-20#7
说实话 这个挺难的 像是 dba 的问题 你是 硕士或者是博士吧。
只能讲讲我的思路,具体code 你要自己 写因为我也不太会。
Dbo.data1 trigger 记录每次 主table 的改变,action 和 值 ,时间等 写在 audit table 。

然后 query 的时候 把现在的 table 根据 audit table 倒叙 做 revert 直到 query 的时间。
比如 audit table 写了 update 了 一个值,那你要 改为原来的值, insert 的row delete, delete 的row insert, 注意顺序必须按 倒叙,这样就能还原 到 原来的 那个时间点 时 那个 table 的 样子。

另外一个思路 是 snap shot, 每次改变table 的时候 trigger snap shot , 记录一个当前database 的 快照, 然后 取的时候 取 snap shot,

cetaphil : 2018-04-20#8
说实话 这个挺难的 像是 dba 的问题 你是 硕士或者是博士吧。
只能讲讲我的思路,具体code 你要自己 写因为我也不太会。
Dbo.data1 trigger 记录每次 主table 的改变,action 和 值 ,时间等 写在 audit table 。
然后 query 的时候 把现在的 table 根据 audit table 倒叙 做 revert 直到 query 的时间。
比如 audit table 写了 update 了 一个值,那你要 改为原来的值, insert 的row delete, delete 的row insert, 注意顺序必须按 倒叙,这样就能还原 到 原来的 那个时间点 时 那个 table 的 样子。

另外一个思路 是 snap shot, 每次改变table 的时候 trigger snap shot , 记录一个当前database 的 快照, 然后 取的时候 取 snap shot,
我是数学专业本科生,辅修了一些计算机课程,没上过数据库这门课,对sql的了解仅限SELECT FROM。。。本来一面的时候问的都是一些基础数据结构Java Javascript Nodejs等等,哪里知道二面的面试官抓着我问SQL,上来就是trigger,完全没学过。他本来要拒绝我了,看在一面面试官极力推荐的份上给我留个题看能不能解出来。。。
面试官给我的思路是也用trigger,他已经把trigger写好了,分别是insert,delete和update,update的时候往audit table里插入旧的数据,我现在要做的就是写那个dbo.data1_PIT function。您的倒叙思路跟我想的一样,只是我还没搞清楚具体该怎么实现。。。
代码:
ALTER TRIGGER [dbo].[trg_data1_Insert_all_fields]

    ON [dbo].[data1]




FOR INSERT

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()
 
    --inserts only - we just need to update user information
    UPDATE u
    SET
        u.[AddLoginID]=@uid,
        u.[AddDate]=getdate()
    FROM [dbo].[data1] u
    INNER JOIN inserted i ON
     i.[date] = u. [date] AND  i.[id_security] = u. [id_security]
    WHERE u.[AddLoginID] IS NULL AND u.[AddDate] IS NULL
代码:
ALTER TRIGGER [dbo].[trg_data1_Delete_all_fields]

    ON [dbo].[data1]

FOR DELETE

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()
 
    -- [ChangeAction]=D means delete
    INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'D' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted
代码:
ALTER TRIGGER [dbo].[trg_data1_Update_all_fields]

    ON [dbo].[data1]

FOR UPDATE
AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()

    IF ( SELECT trigger_nestlevel()
       ) = 1   --don't do anything if triggered from insert trigger
        BEGIN 
        --[ChangeAction]=U means update
        INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'U' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]FROM
        (
        SELECT [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted
        EXCEPT
        SELECT [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM inserted
        ) t
     
        END

fieldmarshal : 2018-04-20#9
说实话 这个挺难的 像是 dba 的问题 你是 硕士或者是博士吧。
只能讲讲我的思路,具体code 你要自己 写因为我也不太会。
Dbo.data1 trigger 记录每次 主table 的改变,action 和 值 ,时间等 写在 audit table 。

然后 query 的时候 把现在的 table 根据 audit table 倒叙 做 revert 直到 query 的时间。
比如 audit table 写了 update 了 一个值,那你要 改为原来的值, insert 的row delete, delete 的row insert, 注意顺序必须按 倒叙,这样就能还原 到 原来的 那个时间点 时 那个 table 的 样子。

另外一个思路 是 snap shot, 每次改变table 的时候 trigger snap shot , 记录一个当前database 的 快照, 然后 取的时候 取 snap shot,
这个很容易!这题都做不了的选手在公司是个废人

zhizhi123 : 2018-04-20#10
这个很容易!这题都做不了的选手在公司是个废人
这么容易,你给把那个pit 的 function 写一下 贴上来呗。 吹牛胡说 谁不会 啊。

fieldmarshal : 2018-04-20#11
这么容易,你给把那个pit 的 function 写一下 贴上来呗。 吹牛胡说 谁不会 啊。
吹牛不动脑轻松你不知道么?哈
不过真的,我是告诉你业界的标准在那里,你要是不信随便你,呵呵
网上很多公开的面经,你看看大公司稍微有点难度的题目!你还可以找几个面试是开卷考试的公司做做
这题做好了应该可以拿到offer如果公司要求不苛刻,但这个难度一般不是压轴题

zhizhi123 : 2018-04-20#12
吹牛不动脑轻松你不知道么?哈
不过真的,我是告诉你业界的标准在那里,你要是不信随便你,呵呵
网上很多公开的面经,你看看大公司稍微有点难度的题目!你还可以找几个面试是开卷考试的公司做做
这题做好了应该可以拿到offer如果公司要求不苛刻,但这个难度一般不是压轴题
啊呸 ,

fieldmarshal : 2018-04-20#13
啊呸 ,
年轻人不服气啊,哈
若干年后你就会明白我说的这都不会的在公司基本是个废人

kirkli : 2018-04-20#14
给你找了个例子,和一个有用的中文网站,希望能有所帮助。
http://www.postgres.cn/docs/10/plpgsql-trigger.html

讨论技术本是好事, 大家相互学习进步。


CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);

CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- 检查给出了 empname 以及 salary
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;

-- 谁会倒贴钱为我们工作?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;

-- 记住谁在什么时候改变了工资单
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;

CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();

道白2 : 2018-04-20#15
我是数学专业本科生,辅修了一些计算机课程,没上过数据库这门课,对sql的了解仅限SELECT FROM。。。本来一面的时候问的都是一些基础数据结构Java Javascript Nodejs等等,哪里知道二面的面试官抓着我问SQL,上来就是trigger,完全没学过。他本来要拒绝我了,看在一面面试官的面子上给我留个题看能不能解出来。。。
面试官给我的思路是也用trigger,他已经把trigger写好了,分别是insert,delete和update,update的时候往audit table里插入旧的数据,我现在要做的就是写那个dbo.data1_PIT function。您的倒叙思路跟我想的一样,只是我还没搞清楚具体该怎么实现。。。
代码:
ALTER TRIGGER [dbo].[trg_data1_Insert_all_fields]

    ON [dbo].[data1]




FOR INSERT

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()
 
    --inserts only - we just need to update user information
    UPDATE u
    SET
        u.[AddLoginID]=@uid,
        u.[AddDate]=getdate()
    FROM [dbo].[data1] u
    INNER JOIN inserted i ON
     i.[date] = u. [date] AND  i.[id_security] = u. [id_security]
    WHERE u.[AddLoginID] IS NULL AND u.[AddDate] IS NULL
代码:
ALTER TRIGGER [dbo].[trg_data1_Delete_all_fields]

    ON [dbo].[data1]

FOR DELETE

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()
 
    -- [ChangeAction]=D means delete
    INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'D' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted
代码:
ALTER TRIGGER [dbo].[trg_data1_Update_all_fields]

    ON [dbo].[data1]

FOR UPDATE
AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()

    IF ( SELECT trigger_nestlevel()
       ) = 1   --don't do anything if triggered from insert trigger
        BEGIN 
        --[ChangeAction]=U means update
        INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'U' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]FROM
        (
        SELECT [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted
        EXCEPT
        SELECT [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM inserted
        ) t
     
        END
好像你都已经做完了,难道你的问题是数据库里的表的函数,是怎么定义的,写在哪里,怎么被调用吗?我感觉你的问题是不太明白ddl(数据定义语言),与dml(数据操纵语言)的写在哪里,怎么被调用,他们的运行机制的问题。

让我们理一下这个过程,
1、建立一个辅助表,分别记录insert, update, delete,这个需要在你说的主表上建立trigger(其实也可以用的别方法), 你已经完成了。
2、从这个辅助表里面,a、把所需的记录根据查询条件取出来,b、要求用一个 function来实现。
a、就是查询语句,就是select * from table where condition in [.......] desc by field, 类似这个样子吧(我好多年不写程序了,记不住精确的语法了:)) 。 这是dml。
b、怎么把a的语句放到一个function里,并将查询条件传入。
---这涉及到function的含义,时间上你可以理解database里有两种function.
b-1:在database里,表可以有自己的function, 定义一个表的function,这个叫ddl。在这个function里可以写dml语言。 但是,表的function里是不接受参数,我好像记得不接受。(当然,各种数据库上可能会有差异)
b-2: storage procedure. 这里定义函数(DDL),接受参数,并返回数据集。我觉得你的问题在存储过程上写一个函数,在函数体内按要求写查询语句,返回结果集就好。
c、一点建议,一般程序员不会重复的去写ddl语句,能看懂就行了。这些ddl都是工具产生的,面试的时候也不会去刻意问你ddl,都是问些dml。你下个navcat或者用access,在上面图像化的界面上操作一下,然后看相应应的dml就明白了。

优化,
简单的说吧,1、patch,只记录不同。然后再function里来生成数据集返回。
2、取决于系统架构和软件结构,是开timer还是用线程,怎么预先生成?等等。。
3、

cetaphil : 2018-04-20#16
好像你都已经做完了,难道你的问题是数据库里的表的函数,是怎么定义的,写在哪里,怎么被调用吗?我感觉你的问题是不太明白ddl(数据定义语言),与dml(数据操纵语言)的写在哪里,怎么被调用,他们的运行机制的问题。

让我们理一下这个过程,
1、建立一个辅助表,分别记录insert, update, delete,这个需要在你说的主表上建立trigger(其实也可以用的别方法), 你已经完成了。
2、从这个辅助表里面,a、把所需的记录根据查询条件取出来,b、要求用一个 function来实现。
a、就是查询语句,就是select * from table where condition in [.......] desc by field, 类似这个样子吧(我好多年不写程序了,记不住精确的语法了:)) 。 这是dml。
b、怎么把a的语句放到一个function里,并将查询条件传入。
---这涉及到function的含义,时间上你可以理解database里有两种function.
b-1:在database里,表可以有自己的function, 定义一个表的function,这个叫ddl。在这个function里可以写dml语言。 但是,表的function里是不接受参数,我好像记得不接受。(当然,各种数据库上可能会有差异)
b-2: storage procedure. 这里定义函数(DDL),接受参数,并返回数据集。我觉得你的问题在存储过程上写一个函数,在函数体内按要求写查询语句,返回结果集就好。
c、一点建议,一般程序员不会重复的去写ddl语句,能看懂就行了。这些ddl都是工具产生的,面试的时候也不会去刻意问你ddl,都是问些dml。你下个navcat或者用access,在上面图像化的界面上操作一下,然后看相应应的dml就明白了。

优化,
简单的说吧,1、patch,只记录不同。然后再function里来生成数据集返回。
2、取决于系统架构和软件结构,是开timer还是用线程,怎么预先生成?等等。。
3、
真的好感动!谢谢您打了这么多字,这还是您的第一个帖子。
我确实是不太会写这个function,主要是sql的syntax不熟悉碰到复杂一些的query就蒙了。现在的思路是,function 设两个parameter @data_date smalldatetime, @as_of_date datetime 先检查在as_of_date时间点之后是否发生了insert delete update,如果没有改变就直接从主表里select,如果有改变就select离as_of_date时间点最近的那个时间点然后return。大概pseudocode写出来了但是不知道怎么写成function.

这是我的 Inline table-valued function,属于语句完全不通状态,算是pseudocode吧。。
还有就是,同一个@data_date会有多个id_security但还没想好怎么处理这点
代码:
CREATE FUNCTION [dbo].[data1_PIT](@data_date smalldatetime, @as_of_date datetime)
RETURNS TABLE
AS
RETURN
SELECT
IF EXISTS (SELECT * FROM data_audit.data1 AS DA WHERE DA.date = @data_date AND DA.ChangeDate > @as_of_date)
        WITH cte AS
        (
            SELECT DA.*, ROW_NUMBER() OVER(PARTITION BY DA.id_security ORDER BY DA.ChangeDate) AS rk
            FROM data_audit.data1 AS DA WHERE DA.date=@data_date AND DA.ChangeDate > @as_of_date)
        )
        SELECT date,
        id_security,
        column6,
        column7,
        column10,
        column11,
        column12,
        column13,
        column14,
        column15,
        column16,
        column17,
        column18,
        column19,
        column20,
        column21,
        column22,
        column23,
        column24,
        column25,
        column26,
        column27,
        column28,
        column29,
        column30,
        column31,
        column32,
        column33,
        column34,
        column35,
        column36,
        column37,
        column38,
        column39,
        column40,
        column41,
        column42,
        column43,
        column44,
        column45,
        AddLoginID,
        AddDate,
        column48,
        column49,
        column50,
        column51,
        column52,
        column53,
        column54,
        column55,
        column56,
        column57,
        column58,
        column59,
        column60,
        column61,
        column62,
        column64,
        column65,
        column66,
        column67,
        column68,
        column69,
        column70,
        column71,
        column72
        FROM cte
        where rk = 1;
ELSE
    SELECT D.* FROM dbo.data1 AS D WHERE date = @data_date
END

cetaphil : 2018-04-20#17
这个很容易!这题都做不了的选手在公司是个废人
大神,可否指导我一下怎么写这个function() ?抓耳挠腮想了半天还是没写出来,下周一还有两个final,现在重新啃sql syntax也来不及了。

zhizhi123 : 2018-04-20#18
good luck

Chinada : 2018-04-20#19
说实话 这个挺难的 像是 dba 的问题 你是 硕士或者是博士吧。
只能讲讲我的思路,具体code 你要自己 写因为我也不太会。
Dbo.data1 trigger 记录每次 主table 的改变,action 和 值 ,时间等 写在 audit table 。

然后 query 的时候 把现在的 table 根据 audit table 倒叙 做 revert 直到 query 的时间。
比如 audit table 写了 update 了 一个值,那你要 改为原来的值, insert 的row delete, delete 的row insert, 注意顺序必须按 倒叙,这样就能还原 到 原来的 那个时间点 时 那个 table 的 样子。

另外一个思路 是 snap shot, 每次改变table 的时候 trigger snap shot , 记录一个当前database 的 快照, 然后 取的时候 取 snap shot,

应该不用倒叙那么复杂,一个复杂查询就行了。

查询audit表的条件:
- 记录时间小于等于函数参数指定的时间
- 每个主键值只取时间最新的那条记录
- 记录的状态不能是删除

主键在这里特指主表里面绝对不会被修改、而且唯一的字段。

cetaphil : 2018-04-20#20
应该不用倒叙那么复杂,一个复杂查询就行了。

查询audit表的条件:
- 记录时间小于等于函数参数指定的时间
- 每个主键只取时间最新的那条记录
- 记录的状态不能是删除

主键在这里特指主表里面绝对不会被修改、而且唯一的字段。
嗯嗯我已经基本上写出query来了,就是不太会sql语法。我的function 设两个parameter @data_date smalldatetime, @as_of_date datetime 先检查在as_of_date时间点之后是否发生了insert delete update,如果没有改变就直接从主表里select,如果有改变就select离as_of_date最近的时间点然后return。大概pseudocode写出来了但是不知道怎么写成function。
代码:
CREATE FUNCTION [dbo].[data1_PIT](@data_date smalldatetime, @as_of_date datetime)
RETURNS TABLE
AS
RETURN
IF EXISTS (SELECT * FROM data_audit.data1 AS DA WHERE DA.date = @data_date AND DA.ChangeDate >= @as_of_date)
    WITH cte AS
        (
            SELECT DA.*, ROW_NUMBER() OVER(PARTITION BY DA.id_security ORDER BY DA.ChangeDate) AS rk
            FROM data_audit.data1 AS DA WHERE DA.date=@data_date AND DA.ChangeDate >= @as_of_date)
        )
        SELECT date,
        id_security,
        column6,
        column7,
        column10,
        column11,
        column12,
        column13,
        column14,
        column15,
        column16,
        column17,
        column18,
        column19,
        column20,
        column21,
        column22,
        column23,
        column24,
        column25,
        column26,
        column27,
        column28,
        column29,
        column30,
        column31,
        column32,
        column33,
        column34,
        column35,
        column36,
        column37,
        column38,
        column39,
        column40,
        column41,
        column42,
        column43,
        column44,
        column45,
        AddLoginID,
        AddDate,
        column48,
        column49,
        column50,
        column51,
        column52,
        column53,
        column54,
        column55,
        column56,
        column57,
        column58,
        column59,
        column60,
        column61,
        column62,
        column64,
        column65,
        column66,
        column67,
        column68,
        column69,
        column70,
        column71,
        column72
        FROM cte
        where rk = 1;
ELSE
    SELECT D.* FROM dbo.data1 AS D WHERE date = @data_date
END

zhizhi123 : 2018-04-20#21
应该不用倒叙那么复杂,一个复杂查询就行了。

查询audit表的条件:
- 记录时间小于等于函数参数指定的时间
- 每个主键值只取时间最新的那条记录
- 记录的状态不能是删除

主键在这里特指主表里面绝对不会被修改、而且唯一的字段。
嘿,你好像是回答的最靠谱的一个人。你说的对。
对于 记录 是 插入或者删除 状态 也是有 办法的。
比如 现在 要查询 2011-12-31 时的 数据。
对于删除, 约定对于主表不允许进行真正的删除,只是对应加一个 field 状态表示 isDeleted.
对于插入,主表加插入时间, 取的时候主表 只取插入时间之前的。
然后 主表 和 audit 表 join

你不需要解释什么是 主键,连主键是什么都不知道的人不适合讨论这个问题。

cetaphil : 2018-04-20#22
大神救命~~~~~~~~

printf : 2018-04-20#23
—— 因为数据库每时每刻都在改变和更新所以在不同时间抓出来的信息不一样。

这个设计思想,使得服务器负担比较重。

如果结合事务日程日志的话好像没必要。

这题目有点象 Rtos 的设计思想?

dimples : 2018-04-20#24
嗯嗯我已经基本上写出query来了,就是不太会sql语法。我的function 设两个parameter @data_date smalldatetime, @as_of_date datetime 先检查在as_of_date时间点之后是否发生了insert delete update,如果没有改变就直接从主表里select,如果有改变就select离as_of_date最近的时间点然后return。大概pseudocode写出来了但是不知道怎么写成function。
代码:
CREATE FUNCTION [dbo].[data1_PIT](@data_date smalldatetime, @as_of_date datetime)
RETURNS TABLE
AS
RETURN
IF EXISTS (SELECT * FROM data_audit.data1 AS DA WHERE DA.date = @data_date AND DA.ChangeDate >= @as_of_date)
    WITH cte AS
        (
            SELECT DA.*, ROW_NUMBER() OVER(PARTITION BY DA.id_security ORDER BY DA.ChangeDate) AS rk
            FROM data_audit.data1 AS DA WHERE DA.date=@data_date AND DA.ChangeDate >= @as_of_date)
        )
        SELECT date,
        id_security,
        column6,
        column7,
        column10,
        column11,
        column12,
        column13,
        column14,
        column15,
        column16,
        column17,
        column18,
        column19,
        column20,
        column21,
        column22,
        column23,
        column24,
        column25,
        column26,
        column27,
        column28,
        column29,
        column30,
        column31,
        column32,
        column33,
        column34,
        column35,
        column36,
        column37,
        column38,
        column39,
        column40,
        column41,
        column42,
        column43,
        column44,
        column45,
        AddLoginID,
        AddDate,
        column48,
        column49,
        column50,
        column51,
        column52,
        column53,
        column54,
        column55,
        column56,
        column57,
        column58,
        column59,
        column60,
        column61,
        column62,
        column64,
        column65,
        column66,
        column67,
        column68,
        column69,
        column70,
        column71,
        column72
        FROM cte
        where rk = 1;
ELSE
    SELECT D.* FROM dbo.data1 AS D WHERE date = @data_date
END
没看明白Function要做什么。
两个tables没有 join 一下? group by [date], [id_security] order by [change_date] ?
我也是菜鸟,

fieldmarshal : 2018-04-20#25
—— 因为数据库每时每刻都在改变和更新所以在不同时间抓出来的信息不一样。

这个设计思想,使得服务器负担比较重。

如果结合事务日程日志的话好像没必要。

这题目有点象 Rtos 的设计思想?
你想太多了。面试官只是随便用个常用场景考一下而已。

dimples : 2018-04-20#26

cetaphil : 2018-04-20#27
思路我都懂,可是因为不会sql语法写不出function,一时半会也学不会。只想先对付一下面试官,如果哪位好心人能告诉我怎么写这个function感激不尽。

cetaphil : 2018-04-20#28
没看明白Function要做什么。
两个tables没有 join 一下? group by [date], [id_security] order by [change_date] ?
我也是菜鸟,
意思就是说每次update insert 和delete主table里的row就会触发trigger,把所做的改动记录在一个叫做audit table的辅table里面,现在要求的function就是给你一个时间点能够让你像穿越历史一样回到那个时间点从主table里面select出内容来。(因为主table里的条目会一直在改变所以不同时间select出的row都不一样)。我的思路是如果在这个时间点之后没有改动,那直接从主table里select,但如果有任何改动,就找离时间点最近的一条从audit里select出返回。现在的问题是我不知道怎么写出来。。看了好多tutorial讲inline table-valued function但是没搞懂到底该怎么写这个function。。

梨花院落溶溶月 : 2018-04-21#29
在数据库里和一般编程不一样,不是一定要写function的。Trigger后面直接写要做的事情就可以。

道白2 : 2018-04-21#30
兄弟啊,我觉得你的sql写的不错啊,怎么能写不出来?是不是把几个问题给mix一块了?
到底是trigger不会写,还是function不会写?还是sql不会写?


按照你的描述,你的trigger已经写好了,也就是所有的历史改变的数据已经放到审计表了,对吗?

如果基于这个前提,你就是对这个已经存在的审计表,写一个查询语句,所谓函数什么的,也就是这个查询语句的马甲。
给你个存储过程的例子,你把字段换成自己的就行了。
CREATE PROCEDURE getauditHistory(@id int, @begintime datatime, @endtime datatime)
begin
declare @str vachar(128)
set @str = 'select * from tablename where id='+ @id +'and' + changetime in ('+@begintime+', '+@endtime+')' order by changetime desc'
exec(@str)
end
-各个数据库系统的ddl和dml语言略有区别,不过你就是刚毕业面试的话, 没人会关心。

如果说你的trigger还没写好,那就是trigger怎么写的问题。
我看你的trigger基本语法都写的挺好的,比我写的强啊。你把trigger改改就行了。
三种操作,分别考虑一下,在做dml时候,把什么样的数据搬到审计表里就行了。
insert, 分为before insert, after insert
update, 分为before update, after update
delete, 这个实际上要考虑你原始表的结构。给个提示,有index字段的话,index被删除后,不能再添加同样的index数值。
(不是所有的db都支持before .., after ...)

至于最后的那个要求,按id加个聚簇索引,如果用id查,对付million,不是大问题。

Mimi2007 : 2018-04-21#31
首先指出我和楼主对题目理解不同的几点:

1. In the future, even though records can be updated or deleted-所以我认为这里只是指Update 和Delete 而不包括insert。
2. I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')-这里的dbo.data1_PIT 是function的名字,不是指原始的table: data1;题目中并没有说history log要从原始table data1中选出. 我是这样理解的,用delete 做例子,既然delete已经发生,原始data1 table里就不会再存在那条信息,又如何能从原始table: data1里select那条信息呢? 别的公司我不知道, 我们公司是原始table delete records, 然后只在audit table 中保留历史记录,用于万一需要revert的时候用。

帮楼主把Trigger 和Function写好了, 也在我电脑上简单测试了一下,运行没问题。 刚毕业都不容易,特别是女生做developer可能会更难一些。祝楼主好运 :wdb10:

---Create Trigger-I used delete as an an example;but this trigger can be modified to use for all update/delete/insert.
Create trigger Demo_Trigger
on data1
for delete
as
begin
set nocount on
insert into data_audit_data1
(date, id_security,changedate,ChangeUser,column6,column7)
select date, id_security,getdate(),SUser_SName(),column6,column7 ---I only used two columns as example, but you can reference all the columns
from deleted
end
go

---Test--Delete any row from data1 to confirm the history log will be inserted into the audit table: data_audit_data1
delete from data1
where date='1976-12-31' and id_security=57 and column6='2030' ---This is the first row in your sample table: data1; you can delete any other row as well.

---Create function: dbo.data1_PIT
Create function dbo.data1_PIT
(@OriginalDate datetime, @ChangeDate datetime)
returns table
as
return
select date,id_security,column6, column7 from data_audit_data1 where date=@OriginalDate and changedate=@ChangeDate
go

---Use function: dbo.data1_PIT to retrieve data that has been deleted above (See attached screenshot below for testing result)

select * from dbo.data1_PIT ('1976-12-31 00:00:00.000','2018-04-21 16:13:52.430')

cetaphil : 2018-04-21#32
首先指出我和楼主对题目理解不同的几点:

1. In the future, even though records can be updated or deleted-所以我认为这里只是指Update 和Delete 而不包括insert。
2. I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')-这里的dbo.data1_PIT 是function的名字,不是指原始的table: data1;题目中并没有说history log要从原始table data1中选出. 我是这样理解的,用delete 做例子,既然delete已经发生,原始data1 table里就不会再存在那条信息,又如何能从原始table: data1里select那条信息呢? 别的公司我不知道, 我们公司是原始table delete records, 然后只在audit table 中保留历史记录,用于万一需要revert的时候用。

帮楼主把Trigger 和Function写好了, 也在我电脑上简单测试了一下,运行没问题。 刚毕业都不容易,特别是女生做developer可能会更难一些。祝楼主好运 :wdb10:

---Create Trigger-I used delete as an an example;but this trigger can be modified to use for all update/delete/insert.
Create trigger Demo_Trigger
on data1
for delete
as
begin
set nocount on
insert into data_audit_data1
(date, id_security,changedate,ChangeUser,column6,column7)
select date, id_security,getdate(),SUser_SName(),column6,column7 ---I only used two columns as example, but you can reference all the columns
from deleted
end
go

---Test--Delete any row from data1 to confirm the history log will be inserted into the audit table: data_audit_data1
delete from data1
where date='1976-12-31' and id_security=57 and column6='2030' ---This is the first row in your sample table: data1; you can delete any other row as well.

---Create function: dbo.data1_PIT
Create function dbo.data1_PIT
(@OriginalDate datetime, @ChangeDate datetime)
returns table
as
return
select date,id_security,column6, column7 from data_audit_data1 where date=@OriginalDate and changedate=@ChangeDate
go

---Use function: dbo.data1_PIT to retrieve data that has been deleted above (See attached screenshot below for testing result)

select * from dbo.data1_PIT ('1976-12-31 00:00:00.000','2018-04-21 16:13:52.430')
谢谢美女姐姐!我看了一下您的function的参数是originalDate和changeDate,但题目要求是dbo.data1_PIT 可以重现过去某时刻的select并return出exact same result,我们是不知道ChangeDate的,只知道我们想重现过去某个时间点主table select出来的数据。三个trigger已经写好了,现在问题就只剩这个function dbo.data1_PIT(@data_date smalldatetime, @as_of_date datetime).
@data_date就是primary key, @as_of_date 是指回到过去某个时间点。

我想的是如果as_of_date之后没有任何变化就直接从主table里面select, 如果有变化就找距离as_of_date最近的ChangeDate,再从audit table里面select。
还有个情况就是同一个data_date有好几个id_security所以对每一个id_security都要找最近的as_of_date。您能否看看怎么实现这个function?
img.png
代码:
USE [store]
GO
/****** Object:  Trigger [dbo].[trg_data1_Delete_all_fields]    Script Date: 4/21/2018 4:51:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trg_data1_Delete_all_fields]

    ON [dbo].[data1]

FOR DELETE

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()
 
    -- [ChangeAction]=D means delete
    INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'D' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted
代码:
USE [store]
GO
/****** Object:  Trigger [dbo].[trg_data1_Insert_all_fields]    Script Date: 4/21/2018 4:52:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [dbo].[trg_data1_Insert_all_fields]

    ON [dbo].[data1]




FOR INSERT

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()
 
    --inserts only - we just need to update user information
    UPDATE u
    SET
        u.[AddLoginID]=@uid,
        u.[AddDate]=getdate()
    FROM [dbo].[data1] u
    INNER JOIN inserted i ON
     i.[date] = u. [date] AND  i.[id_security] = u. [id_security]
    WHERE u.[AddLoginID] IS NULL AND u.[AddDate] IS NULL
代码:
USE [store]
GO
/****** Object:  Trigger [dbo].[trg_data1_Update_all_fields]    Script Date: 4/21/2018 4:52:41 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [dbo].[trg_data1_Update_all_fields]

    ON [dbo].[data1]

FOR UPDATE
AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()

    IF ( SELECT trigger_nestlevel()
       ) = 1   --don't do anything if triggered from insert trigger
        BEGIN 
        --[ChangeAction]=U means update
        INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'U' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]FROM
        (
        SELECT [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted
        EXCEPT
        SELECT [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM inserted
        ) t
     
        END

Mimi2007 : 2018-04-21#33
谢谢美女姐姐!我看了一下您的function的参数是originalDate和changeDate,但题目要求是dbo.data1_PIT 可以重现过去某时刻的select并return出exact same result,我们是不知道ChangeDate的,只知道我们想重现过去某个时间点主table select出来的数据。三个trigger已经写好了,现在问题就只剩这个function dbo.data1_PIT(@data_date smalldatetime, @as_of_date datetime).
@data_date就是primary key, @as_of_date 是指回到过去某个时间点。

我想的是如果as_of_date之后没有任何变化就直接从主table里面select, 如果有变化就找距离as_of_date最近的ChangeDate,再从audit table里面select。
还有个情况就是同一个data_date有好几个id_security所以对每一个id_security都要找最近的as_of_date。您能否看看怎么实现这个function?
浏览附件493410
代码:
USE [store]
GO
/****** Object:  Trigger [dbo].[trg_data1_Delete_all_fields]    Script Date: 4/21/2018 4:51:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trg_data1_Delete_all_fields]

    ON [dbo].[data1]

FOR DELETE

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()

    -- [ChangeAction]=D means delete
    INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'D' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted
代码:
USE [store]
GO
/****** Object:  Trigger [dbo].[trg_data1_Insert_all_fields]    Script Date: 4/21/2018 4:52:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [dbo].[trg_data1_Insert_all_fields]

    ON [dbo].[data1]




FOR INSERT

AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()

    --inserts only - we just need to update user information
    UPDATE u
    SET
        u.[AddLoginID]=@uid,
        u.[AddDate]=getdate()
    FROM [dbo].[data1] u
    INNER JOIN inserted i ON
     i.[date] = u. [date] AND  i.[id_security] = u. [id_security]
    WHERE u.[AddLoginID] IS NULL AND u.[AddDate] IS NULL
代码:
USE [store]
GO
/****** Object:  Trigger [dbo].[trg_data1_Update_all_fields]    Script Date: 4/21/2018 4:52:41 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [dbo].[trg_data1_Update_all_fields]

    ON [dbo].[data1]

FOR UPDATE
AS

    SET NOCOUNT ON
    DECLARE @uid INT
    SET @uid = SUSER_ID()

    IF ( SELECT trigger_nestlevel()
       ) = 1   --don't do anything if triggered from insert trigger
        BEGIN
        --[ChangeAction]=U means update
        INSERT INTO [data_audit].[data1] ( [ChangeAction], [ChangeDate], [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72] )
        SELECT 'U' AS [ChangeAction],getdate() AS [ChangeDate], @uid AS [ChangeUser], [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]FROM
        (
        SELECT [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM deleted
        EXCEPT
        SELECT [date],[id_security],[column6],[column7],[column10],[column11],[column12],[column13],[column14],[column15],[column16],[column17],[column18],[column19],[column20],[column21],[column22],[column23],[column24],[column25],[column26],[column27],[column28],[column29],[column30],[column31],[column32],[column33],[column34],[column35],[column36],[column37],[column38],[column39],[column40],[column41],[column42],[column43],[column44],[column45],[AddLoginID],[AddDate],[column48],[column49],[column50],[column51],[column52],[column53],[column54],[column55],[column56],[column57],[column58],[column59],[column60],[column61],[column62],[column64],[column65],[column66],[column67],[column68],[column69],[column70],[column71],[column72]
        FROM inserted
        ) t

        END

我不大明白你的意思。题目要求是有提供changedate 的。

你的题目:
In the future, even though records can be updated or deleted, if
  • I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')
  • I will have to get identical output as I see now with the query under a.a (SELECT * FROM dbo.data1 WHERE date = '2011-12-31')
你的题目的第一点 “dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')”提供了2个时间点, 第二个应该是 changedate, 所以面试官应该是是提供了changedate 的。这个是原题吗?还是你自己的理解?如果是你自己的理解而不是原题的话,建议你把原本的题目贴出来看一下。

cetaphil : 2018-04-21#34
我不大明白你的意思。题目要求是有提供changedate 的。

你的题目:
In the future, even though records can be updated or deleted, if
  • I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')
  • I will have to get identical output as I see now with the query under a.a (SELECT * FROM dbo.data1 WHERE date = '2011-12-31')
你的题目的第一点提供了2个时间点。这个是原题吗?还是你自己的理解?如果是你自己的理解而不是原题的话,建议你把原本的题目贴出来看一下。
我之前附的这张图就是原题,trigger面试官已经写好了,现在只剩implement function。
他的意思是,假设在2018-02-22 14:42:22这个时间,我去主table select出一些东西,设这些东西叫做A。这些东西会update/delete并记录在audit table里面。那么今天是2018-04-21,我输入SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22'),我希望还能return出A。第二个参数不是changeDate,而是我想重现的那个时间点。

xiaoa : 2018-04-21#35
我不大明白你的意思。题目要求是有提供changedate 的。

你的题目:
In the future, even though records can be updated or deleted, if
  • I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')
  • I will have to get identical output as I see now with the query under a.a (SELECT * FROM dbo.data1 WHERE date = '2011-12-31')
你的题目的第一点 “dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')”提供了2个时间点, 第二个应该是 changedate, 所以面试官应该是是提供了changedate 的。这个是原题吗?还是你自己的理解?如果是你自己的理解而不是原题的话,建议你把原本的题目贴出来看一下。

你理解非常错误,楼主理解是正确的,亏你是dba .
是这样的,假设当前时刻 是 2018-02-22 14:42:22 ,
我执行 SELECT * FROM dbo.data1 WHERE date = '2011-12-31') 得到了一些数据,
之后 这些数据 有改变, 具体改变 以及改变的时间在 audit table 里, audit table 里的changedate 就是 改变发生的时间。
那么假设过了几天, 当前时刻变成 2018-04-21 14:42:22 , 如果同样 执行 SELECT * FROM dbo.data1 WHERE date = '2011-12-31') , 得到的结果肯定 和前几天得到的结果不一样。
要求是 写个 function data1_PIT , 使得 SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22') ( 第二个parameter 是上次执行sql 的时间),
使得返回的结果和上次 时刻 (2018-02-22 14:42:22 ) 时 得到的结果一致。
这种 database 设计需要 符合 6NF , https://en.wikipedia.org/wiki/Sixth_normal_form

Mimi2007 : 2018-04-21#36
你理解非常错误,楼主理解是正确的,亏你是dba .
是这样的,假设当前时刻 是 2018-02-22 14:42:22 ,
我执行 SELECT * FROM dbo.data1 WHERE date = '2011-12-31') 得到了一些数据,
之后 这些数据 有改变, 具体改变 以及改变的时间在 audit table 里, audit table 里的changedate 就是 改变发生的时间。
那么假设过了几天, 当前时刻变成 2018-04-21 14:42:22 , 如果同样 执行 SELECT * FROM dbo.data1 WHERE date = '2011-12-31') , 得到的结果肯定 和前几天得到的结果不一样。
要求是 写个 function data1_PIT , 使得 SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22') ( 第二个parameter 是上次执行sql 的时间),
使得返回的结果和上次 时刻 (2018-02-22 14:42:22 ) 时 得到的结果一致。
这种 database 设计需要 符合 6NF , https://en.wikipedia.org/wiki/Sixth_normal_form


我是按英文这部分来理解给的建议。我的理解可能是错的,不过我也只是好心给出我的看法而已。我们做project 的话都有详细的project doc, 所以不存在理解有分歧的情况。因为如果什么理解有歧义,在写code 之前就会找 stakeholder 问明白了。呵呵。我
不是很理解楼主的题目,所以不好意思帮不到楼主。之前楼主说不会写function的 syntax, 我的例子中至少syntax 是对的,楼主可以按你的理解改一下来用吧。

面试题是:Create SQL objects to return Point-in-time data that generate past results
  1. Create SQL object to show data in the same structure as dbo.data1 but as of certain date/time.
To be more clear
  1. Now time is 2018-02-22 14:42:22.
    • I do SELECT * FROM dbo.data1 WHERE date = '2011-12-31'
    • and get some records.
  2. In the future, even though records can be updated or deleted, if
    • I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')
    • I will have to get identical output as I see now with the query under a.a (SELECT * FROM dbo.data1 WHERE date = '2011-12-31')
  3. Based on the code in triggers, I would like you to elaborate on:
    • Our approach to data audits. Triggers and PIT functions are generated automatically with stored procedure, so maintenance is not a problem.
    • Ideas for improvement. There is certainly room for improvement.
    • Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.
    • Any suggestions for audit table compression and/or partitioning.

Mimi2007 : 2018-04-21#37
你理解非常错误,楼主理解是正确的,亏你是dba .
是这样的,假设当前时刻 是 2018-02-22 14:42:22 ,
我执行 SELECT * FROM dbo.data1 WHERE date = '2011-12-31') 得到了一些数据,
之后 这些数据 有改变, 具体改变 以及改变的时间在 audit table 里, audit table 里的changedate 就是 改变发生的时间。
那么假设过了几天, 当前时刻变成 2018-04-21 14:42:22 , 如果同样 执行 SELECT * FROM dbo.data1 WHERE date = '2011-12-31') , 得到的结果肯定 和前几天得到的结果不一样。
要求是 写个 function data1_PIT , 使得 SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22') ( 第二个parameter 是上次执行sql 的时间),
使得返回的结果和上次 时刻 (2018-02-22 14:42:22 ) 时 得到的结果一致。
这种 database 设计需要 符合 6NF , https://en.wikipedia.org/wiki/Sixth_normal_form

顺便说一下,我可从来没说过我是DBA。我离DBA还差老远了呢。

cetaphil : 2018-04-21#38

我是按英文这部分来理解给的建议。我的理解可能是错的,不过我也只是好心给出我的看法而已。我们做project 的话都有详细的project doc, 所以不存在理解有分歧的情况。因为如果什么理解有歧义,在写code 之前就会找 stakeholder 文明白了,呵呵。我是不是很理解楼主的题目,所以不好意思帮不到楼主。之前楼主说不会写function的 syntax, 我的例子中至少syntax 是对的,楼主可以按你的理解改一下来用吧。

面试题是:Create SQL objects to return Point-in-time data that generate past results
  1. Create SQL object to show data in the same structure as dbo.data1 but as of certain date/time.
To be more clear
  1. Now time is 2018-02-22 14:42:22.
    • I do SELECT * FROM dbo.data1 WHERE date = '2011-12-31'
    • and get some records.
  2. In the future, even though records can be updated or deleted, if
    • I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')
    • I will have to get identical output as I see now with the query under a.a (SELECT * FROM dbo.data1 WHERE date = '2011-12-31')
  3. Based on the code in triggers, I would like you to elaborate on:
    • Our approach to data audits. Triggers and PIT functions are generated automatically with stored procedure, so maintenance is not a problem.
    • Ideas for improvement. There is certainly room for improvement.
    • Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.
    • Any suggestions for audit table compression and/or partitioning.
美女姐姐别生气,我说的有些绕,xiaoa表达的比我清楚。您的思路是对的,只是跟面试官的要求不太一致。面试官后来还加了一句hint:
I also wanted to give you few hints about my own solution:

I decided to use Table-Valued User-Defined Function

I created function: dbo.data1_PIT(@data_date smalldatetime, @as_of_date datetime).

The function returns data for one date, in structure identical to the structure of dbo.data1

According to my simple measurements, overhead of SELECT * statement over our live table is just 10%.
我再看看tutorial怎么写function吧。这个操作的话inline table-valued function可以实现吗?我要从两个table里select的话是不是要写multi statement的那种?

xiaoa : 2018-04-21#39
顺便说一下,我可从来没说过我是DBA。我离DBA还差老远了呢。
你长得一定非常漂亮.

Mimi2007 : 2018-04-21#40
你长得一定非常漂亮.

是因为长得漂亮的人写Code都很糟糕吗?呵呵。我写Code也只有两年,初级水平,还在努力进步中,见笑了。

xiaoa : 2018-04-21#41
是因为长得漂亮的人写Code都很糟糕吗?呵呵。我写Code也只有两年,初级水平,还在努力进步中,见笑了。
我自己都6年 full stack developer , 而且自认作数据库还可以,都没做对。
你爱热心帮助人,还挺自信,这只能是来自 要么你是大神,大牛,要么你长得非常漂亮。
你逻辑一般,所以.....

Mimi2007 : 2018-04-21#42
我自己都6年 full stack developer , 而且自认作数据库还可以,都没做对。
你爱热心帮助人,还挺自信,这只能是来自 要么你是大神,大牛,要么你长得非常漂亮。
你逻辑一般,所以.....

我的头像是我本人。不过每个人审美不同,所以我也不敢说我很漂亮。我是学会计出身,所以写code前要问好多问题确定stakeholders 要什么结果才开始写,也算是文科生的一个毛病吧。不过一旦我理解了要什么结果,写的Code还是不太差的,呵呵。

kirkli : 2018-04-21#43
首先指出我和楼主对题目理解不同的几点:

1. In the future, even though records can be updated or deleted-所以我认为这里只是指Update 和Delete 而不包括insert。
2. I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')-这里的dbo.data1_PIT 是function的名字,不是指原始的table: data1;题目中并没有说history log要从原始table data1中选出. 我是这样理解的,用delete 做例子,既然delete已经发生,原始data1 table里就不会再存在那条信息,又如何能从原始table: data1里select那条信息呢? 别的公司我不知道, 我们公司是原始table delete records, 然后只在audit table 中保留历史记录,用于万一需要revert的时候用。

帮楼主把Trigger 和Function写好了, 也在我电脑上简单测试了一下,运行没问题。 刚毕业都不容易,特别是女生做developer可能会更难一些。祝楼主好运 :wdb10:

---Create Trigger-I used delete as an an example;but this trigger can be modified to use for all update/delete/insert.
Create trigger Demo_Trigger
on data1
for delete
as
begin
set nocount on
insert into data_audit_data1
(date, id_security,changedate,ChangeUser,column6,column7)
select date, id_security,getdate(),SUser_SName(),column6,column7 ---I only used two columns as example, but you can reference all the columns
from deleted
end
go

---Test--Delete any row from data1 to confirm the history log will be inserted into the audit table: data_audit_data1
delete from data1
where date='1976-12-31' and id_security=57 and column6='2030' ---This is the first row in your sample table: data1; you can delete any other row as well.

---Create function: dbo.data1_PIT
Create function dbo.data1_PIT
(@OriginalDate datetime, @ChangeDate datetime)
returns table
as
return
select date,id_security,column6, column7 from data_audit_data1 where date=@OriginalDate and changedate=@ChangeDate
go

---Use function: dbo.data1_PIT to retrieve data that has been deleted above (See attached screenshot below for testing result)

select * from dbo.data1_PIT ('1976-12-31 00:00:00.000','2018-04-21 16:13:52.430')

这位美女做的挺好的,简明易懂。

1.根据原始表的删除修改等变化进而触发trigger在audit表里面做记录。
2. 创建查询audit记录的function
3.调用这个function浏览结果

cetaphil : 2018-04-21#44
这位美女做的挺好的,简明易懂。

1.根据原始表的删除修改等变化进而触发trigger在audit表里面做记录。
2. 创建查询audit记录的function
3.调用这个function浏览结果
Mimi写的第二个parameter changeDate是数据更改并记录进audit的时间,然而面试官要求的parameter是上次进行select操作的时间

xiaoa : 2018-04-21#45
Mimi写的第二个parameter changeDate是数据更改并记录进audit的时间,然而面试官要求的parameter是上次进行select操作的时间
所以你要改她的那个 function 里面的逻辑。

cetaphil : 2018-04-21#46
所以你要改她的那个 function 里面的逻辑。
然鹅我不会写
代码:
CREATE FUNCTION [dbo].[data1_PIT](@data_date smalldatetime, @as_of_date datetime)
RETURNS TABLE
AS
RETURN
IF EXISTS (SELECT * FROM data_audit.data1 AS DA WHERE DA.date = @data_date AND DA.ChangeDate >= @as_of_date)
    WITH cte AS
        (
            SELECT DA.*, ROW_NUMBER() OVER(PARTITION BY DA.id_security ORDER BY DA.ChangeDate) AS rk
            FROM data_audit.data1 AS DA WHERE DA.date=@data_date AND DA.ChangeDate >= @as_of_date)
        )
        SELECT date,
        id_security,
        column6,
        column7,
        FROM cte
        where rk = 1;
ELSE
    SELECT D.* FROM dbo.data1 AS D WHERE date = @data_date
END
。。我写了一个query但是不知道怎么变成function,而且我还没有完全考虑不同security id的问题,您看看能修改一下吗?

fieldmarshal : 2018-04-21#47
我自己都6年 full stack developer , 而且自认作数据库还可以,都没做对。
你爱热心帮助人,还挺自信,这只能是来自 要么你是大神,大牛,要么你长得非常漂亮。
你逻辑一般,所以.....
和美女搭讪也不能光靠贬低自己水平……

kirkli : 2018-04-21#48
mimi已经做出来了并且简明易懂,唯一个逻辑上需要修改的地方红色部分已经指出
打个比方现在是 2019年1月1日,我们要查询2018年4月21日那个时候的结果。 那么在function里面 @ChangeDate = '2018-04-21 0:0:0.000', 我们只需要在函数里面限定changedate<=@ChangeDate 就可以了。
因为任何在2018-04-21之后的修改删除都是新的纪录纪录在deleted表里面。跟你要查询的这个时间无关。

仅供参考。

多谢mimi的code。




---Create Trigger
-I used delete as an an example;but this trigger can be modified to use for all update/delete/insert.
Create trigger Demo_Trigger
on data1
after delete,update
as
begin
set nocount on
insert into audit_data1
(date, id_security,changedate,ChangeUser,column6,column7)
select date, id_security,getdate(),ChangeUserName,column6,column7 ---I only used two columns as example, but you can reference all the columns
from deleted
end
go

---Test--Delete any row from data1 to confirm the history log will be inserted into the audit table: data_audit_data1
delete from data1
where date='1976-12-31' and id_security=57 and column6='2030' ---This is the first row in your sample table: data1; you can delete any other row as well.

---Create function: dbo.data1_PIT
Create function dbo.data1_PIT
(@OriginalDate datetime, @ChangeDate datetime)
returns table
as
return
select date,id_security,column6, column7 from audit_data1 where date=@OriginalDate and changedate<=@ChangeDate
go

---Use function: dbo.data1_PIT to retrieve data that has been deleted above (See attached screenshot below for testing result)

select * from dbo.data1_PIT ('1976-12-31 00:00:00.000','2018-04-21 16:13:52.430')

xiaoa : 2018-04-21#49
和美女搭讪也不能光靠贬低自己水平……
大爷,我就这一招。 你行你up 给我们看看 怎么搭讪。

楼上的,如果有一个记录从来没被改过,所以肯定不在 audit table 里, 你这样选出来的 table 肯定不包含这个记录,所以....
如果有一个记录被改过2次,所以在 audit table 里 有两个记录,你这样选出来的 2个都有。 这肯定也不对。
等等。 所以.....

cetaphil : 2018-04-21#50
mimi已经做出来了并且简明易懂,唯一个逻辑上需要修改的地方红色部分已经指出
打个比方现在是 2019年1月1日,我们要查询2018年4月21日那个时候的结果。 那么在function里面 @ChangeDate = '2018-04-21 0:0:0.000', 我们只需要在函数里面限定changedate<=@ChangeDate 就可以了。
因为任何在2018-04-21之后的修改删除都是新的纪录纪录在deleted表里面。跟你要查询的这个时间无关。

仅供参考。

多谢mimi的code。




---Create Trigger
-I used delete as an an example;but this trigger can be modified to use for all update/delete/insert.
Create trigger Demo_Trigger
on data1
after delete,update
as
begin
set nocount on
insert into audit_data1
(date, id_security,changedate,ChangeUser,column6,column7)
select date, id_security,getdate(),ChangeUserName,column6,column7 ---I only used two columns as example, but you can reference all the columns
from deleted
end
go

---Test--Delete any row from data1 to confirm the history log will be inserted into the audit table: data_audit_data1
delete from data1
where date='1976-12-31' and id_security=57 and column6='2030' ---This is the first row in your sample table: data1; you can delete any other row as well.

---Create function: dbo.data1_PIT
Create function dbo.data1_PIT
(@OriginalDate datetime, @ChangeDate datetime)
returns table
as
return
select date,id_security,column6, column7 from audit_data1 where date=@OriginalDate and changedate<=@ChangeDate
go

---Use function: dbo.data1_PIT to retrieve data that has been deleted above (See attached screenshot below for testing result)

select * from dbo.data1_PIT ('1976-12-31 00:00:00.000','2018-04-21 16:13:52.430')
您写的changedate<=@ChangeDate会把这个row自创建以来一直到@ChangeDate的所有update/delete的log都return出来的,根据要求,我们只想return出一条row。而且应该是在@ChangeDate之后的第一条row。原因是无论是delete还是update存进audit table都是被删除的row。
我的表达能力不太好,就举个栗子:
假设这条row是2018-04-20日创建的,我们在2018-04-21 做了select * from dbo.data1 where date = '2018-04-20', 接着这条row马上被更新了好几次,这个时候存进audit table里面的是这条row每次被更新之前的状态(更新之后的就update在主table里面)那么我们在2018-02-22重新做select * from dbo.data1 where date = '2018-04-20'的话,我们看到的是更新之后的状态。如果想看到同在2018-04-21 return出来一样的row,我们只能去audit table里面找2018-04-21之后的第一条记录。
还有一种情况是2018-04-21后这个table没有做过改变,那么我们只要从主table里面select就可以了。不知道解释是否清楚。

Mimi2007 : 2018-04-21#51


美女姐姐别生气,我说的有些绕,xiaoa表达的比我清楚。您的思路是对的,只是跟面试官的要求不太一致。面试官后来还加了一句hint:
I also wanted to give you few hints about my own solution:

I decided to use Table-Valued User-Defined Function

I created function: dbo.data1_PIT(@data_date smalldatetime, @as_of_date datetime).

The function returns data for one date, in structure identical to the structure of dbo.data1

According to my simple measurements, overhead of SELECT * statement over our live table is just 10%.
我再看看tutorial怎么写function吧。这个操作的话inline table-valued function可以实现吗?我要从两个table里select的话是不是要写multi statement的那种?

我没有生气啊。理解不同是常有的事。我在公司做Project的时候也会有和Project manger理解不同的时候,所以有时候即使code 写出来,还是要重新改过的,呵呵。

我按照你的思路改了一下我之前写的function,你看这回满足你们面试官的要求吗? 我明天要早起出门,只能帮你到这里了,祝你好运!

---Create dbo.data1_PIT function
alter function dbo.data1_PIT
(@data_date smalldatetime, @as_of_date datetime)
returns @history table
(
date datetime, id_security int, column6 varchar (100),column7 varchar (100)
)
as
begin
if (select count(*) from data_audit_data1 where date=@data_date and changedate>=@as_of_date)>0
begin
insert into @history
select date, id_security, column6, column7 from data_audit_data1
where changedate=(select min(changedate) from data_audit_data1
where changedate>@as_of_date and date=@data_date
group by date)
end
else
insert into @history
select date, id_security, column6, column7 from data1 where date=@data_date
return
end
go

---Retrive data
select * from dbo.data1_PIT ('1976-12-31 00:00:00.000','2018-02-22 14:42:22')

道白2 : 2018-04-21#52
我是按英文这部分来理解给的建议。我的理解可能是错的,不过我也只是好心给出我的看法而已。我们做project 的话都有详细的project doc, 所以不存在理解有分歧的情况。因为如果什么理解有歧义,在写code 之前就会找 stakeholder 问明白了。呵呵。我
不是很理解楼主的题目,所以不好意思帮不到楼主。之前楼主说不会写function的 syntax, 我的例子中至少syntax 是对的,楼主可以按你的理解改一下来用吧。

面试题是:Create SQL objects to return Point-in-time data that generate past results
  1. Create SQL object to show data in the same structure as dbo.data1 but as of certain date/time.
To be more clear
  1. Now time is 2018-02-22 14:42:22.
    • I do SELECT * FROM dbo.data1 WHERE date = '2011-12-31'
    • and get some records.
  2. In the future, even though records can be updated or deleted, if
    • I do SELECT * FROM dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')
    • I will have to get identical output as I see now with the query under a.a (SELECT * FROM dbo.data1 WHERE date = '2011-12-31')
  3. Based on the code in triggers, I would like you to elaborate on:
    • Our approach to data audits. Triggers and PIT functions are generated automatically with stored procedure, so maintenance is not a problem.
    • Ideas for improvement. There is certainly room for improvement.
    • Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.
    • Any suggestions for audit table compression and/or partitioning.
给你点个赞。你做的已经足够好了。
技术无对错。这个对错并非是技术问题本身没有是非曲直。而是说技术讨论中的对错标准,往往掺杂了讨论者自身的技术能力,理解水平、判断标准和个人素养,最终的讨论往往偏离了 技术问题本身的对与错。
所以不用在意技术问题之外评价,做自己想做的事情,帮能帮的事情就好。

Mimi2007 : 2018-04-21#53
给你点个赞。你做的已经足够好了。
技术无对错。这个对错并非是技术问题本身没有是非曲直。而是说技术讨论中的对错标准,往往掺杂了讨论者自身的技术能力,理解水平、判断标准和个人素养,最终的讨论往往偏离了 技术问题本身的对与错。
所以不用在意技术问题之外评价,做自己想做的事情,帮能帮的事情就好。

谢谢道白兄点赞,:wdb6:

dimples : 2018-04-21#54
呵呵,Mimi也是Developer啊,赞。
audit log table在workflow应用中非常常见。我若面试楼主,倒想问问如何优化这个table。

Mimi2007 : 2018-04-21#55
呵呵,Mimi也是Developer啊,赞。
audit log table在workflow应用中非常常见。我若面试楼主,倒想问问如何优化这个table。

是呀,我也是做developer的,小码农一枚,呵呵。

dimples : 2018-04-21#56
是呀,我也是做developer的,小码农一枚,呵呵。
我也是,混碗饭吃。以后帖里还要请Mimi多多指教。

Mimi2007 : 2018-04-21#57
我也是,混碗饭吃。以后帖里还要请Mimi多多指教。

大叔也是Developer哦,真巧。我经验还少,连senior 都没做上呢。希望是我向你请教才是,呵呵。

dimples : 2018-04-21#58
大叔也是Developer哦,真巧。我经验还少,连senior 都没做上呢。希望是我向你请教才是,呵呵。
你谦虚。软件行业是年轻人的天下。
我们这种中年移民做计算机,无非是通过工作求生存。
而你们年轻人,可以实现个人抱负,造就一番大事业。

Mimi2007 : 2018-04-21#59
你谦虚。软件行业是年轻人的天下。
我们这种中年移民做计算机,无非是通过工作求生存。
而你们年轻人,可以实现个人抱负,造就一番大事业。

呵呵,我没那么大野心。对我来说也只是一份工作而已。高薪就更好了,:wdb6:。大叔我闪人睡觉去了,你也早点休息。改天聊。

foreverjia : 2018-04-22#60
感觉目前回答的思路好像都不大对,简单说说我的想法:
1. 第一个 WHERE date = '2011-12-31', 不要管他,这个给人带了很多confusion, 你可以把它当成任何一个Where Condition, 就像Where CompanyName='xxx'.
反正是得到一个DataSet, 这个时间点可以完全忽略。

2. 两个关键的时间点,第一个是ChangeDate, 像题目中的'2018-02-22 14:42:22', 是Hisotry Date, 另外一个其实是隐含的时间点,题目中没有给出了,但实际上
很重要,就是当前的时间点,CurrentTime = GetDate()

3. 所有的DataSet还是只能从主DataTable里选取,这是唯一的Source,但是主DataTable里只有当前时间点的数据,所以也要用到Audit Table.

4. 基本思路就是: SELECT * FROM dbo.data1 WHERE date = '2011-12-31', 得到当前时间点的DataSet, 然后再Revert所有的Changes between History Data between Current Time from audit Table:
分三种:Insert的必须要Exclude, Delete的要Union起来,Update的要找回原值。

SELECT * FROM dbo.data1 WHERE date = '2011-12-31' AND PrimaryID NOT IN (SELECT PrimaryID FROM data_audit_data1 WHERE date = '2011-12-31' and TriggerType='Insert' and ChangeDate > '2018-02-22 14:42:22' )
UNION
SELECT * FROM data_audit_data1 WHERE date = '2011-12-31' and TriggerType='Delete' and ChangeDate > '2018-02-22 14:42:22'

这个可以把Hisotry Data和Current Time之间的 Insert和Delete给 Revert回去,

还有一个Update的比较麻烦一点,因为同样一个PrimaryID可能会被Update几次,应该是只用最早时间的的一次Record的原值,这个部分就懒得写了,把这部分和上面的DataSet合并
,就得到你想要的结果了。

上面的所有的只是思路和抽象代码,你理解了剩下的应该很容易写出来的。

cetaphil : 2018-04-24#61
感觉目前回答的思路好像都不大对,简单说说我的想法:
1. 第一个 WHERE date = '2011-12-31', 不要管他,这个给人带了很多confusion, 你可以把它当成任何一个Where Condition, 就像Where CompanyName='xxx'.
反正是得到一个DataSet, 这个时间点可以完全忽略。

2. 两个关键的时间点,第一个是ChangeDate, 像题目中的'2018-02-22 14:42:22', 是Hisotry Date, 另外一个其实是隐含的时间点,题目中没有给出了,但实际上
很重要,就是当前的时间点,CurrentTime = GetDate()

3. 所有的DataSet还是只能从主DataTable里选取,这是唯一的Source,但是主DataTable里只有当前时间点的数据,所以也要用到Audit Table.

4. 基本思路就是: SELECT * FROM dbo.data1 WHERE date = '2011-12-31', 得到当前时间点的DataSet, 然后再Revert所有的Changes between History Data between Current Time from audit Table:
分三种:Insert的必须要Exclude, Delete的要Union起来,Update的要找回原值。

SELECT * FROM dbo.data1 WHERE date = '2011-12-31' AND PrimaryID NOT IN (SELECT PrimaryID FROM data_audit_data1 WHERE date = '2011-12-31' and TriggerType='Insert' and ChangeDate > '2018-02-22 14:42:22' )
UNION
SELECT * FROM data_audit_data1 WHERE date = '2011-12-31' and TriggerType='Delete' and ChangeDate > '2018-02-22 14:42:22'

这个可以把Hisotry Data和Current Time之间的 Insert和Delete给 Revert回去,

还有一个Update的比较麻烦一点,因为同样一个PrimaryID可能会被Update几次,应该是只用最早时间的的一次Record的原值,这个部分就懒得写了,把这部分和上面的DataSet合并
,就得到你想要的结果了。

上面的所有的只是思路和抽象代码,你理解了剩下的应该很容易写出来的。
感谢,前两天忙着期末考没时间写,我照着您的代码和楼上各位数据库大神的指导差不多写出来了。
代码:
ALTER FUNCTION [dbo].[data1_PIT](@data_date smalldatetime, @as_of_date datetime)
RETURNS TABLE
AS
RETURN
(
    WITH TMP_1 AS (SELECT  *, ROW_NUMBER() OVER (PARTITION BY id_security ORDER BY ChangeDate ASC) rn
FROM  data_audit.data1 WHERE date = @data_date AND ChangeDate >=  @as_of_date),
TMP_2 AS (SELECT * FROM TMP_1 WHERE rn = 1)
SELECT * FROM data1
    WHERE date = @data_date
    AND id_security NOT IN (SELECT id_security FROM TMP_2)
    AND AddDate <= @as_of_date
UNION
SELECT
    date,id_security,column6,column7,column10,column11,column12,column13,column14,column15,column16,column17,column18,column19,column20,column21,column22,
    column23,column24,column25,column26,column27,column28,column29,column30,column31,column32,column33,column34,column35,column36,column37,column38,column39,
    column40,column41,column42,column43,column44,column45,AddLoginID,AddDate,column48,column49,column50,column51,column52,column53,column54,column55,column56,
    column57,column58,column59,column60,column61,column62,column64,column65,column66,column67,column68,column69,column70,column71,column72 
FROM TMP_2
)

fieldmarshal : 2018-04-24#62
感谢,前两天忙着期末考没时间写,我照着您的代码和楼上各位数据库大神的指导差不多写出来了。
代码:
ALTER FUNCTION [dbo].[data1_PIT](@data_date smalldatetime, @as_of_date datetime)
RETURNS TABLE
AS
RETURN
(
    WITH TMP_1 AS (SELECT  *, ROW_NUMBER() OVER (PARTITION BY id_security ORDER BY ChangeDate ASC) rn
FROM  data_audit.data1 WHERE date = @data_date AND ChangeDate >=  @as_of_date),
TMP_2 AS (SELECT * FROM TMP_1 WHERE rn = 1)
SELECT * FROM data1
    WHERE date = @data_date
    AND id_security NOT IN (SELECT id_security FROM TMP_2)
    AND AddDate <= @as_of_date
UNION
SELECT
    date,id_security,column6,column7,column10,column11,column12,column13,column14,column15,column16,column17,column18,column19,column20,column21,column22,
    column23,column24,column25,column26,column27,column28,column29,column30,column31,column32,column33,column34,column35,column36,column37,column38,column39,
    column40,column41,column42,column43,column44,column45,AddLoginID,AddDate,column48,column49,column50,column51,column52,column53,column54,column55,column56,
    column57,column58,column59,column60,column61,column62,column64,column65,column66,column67,column68,column69,column70,column71,column72
FROM TMP_2
)
这sql……天啦......

cetaphil : 2018-04-24#63
这sql……天啦......
大神能不能指点一二?我是真的不会写。

cetaphil : 2018-05-01#64
跟大家汇报一下

在星期六童鞋惊呼的天啦。。。又不肯提供帮助的情况下,我硬着头皮把作业交了上去。很快面试官就打来电话说非常满意并约好了下次面试的时间。前后一共三次技术面一次行为面一次笔试,终于拿到了offer。

昨天考完了最后一门课的期末考,结束了大学四年的全部考试。这段时间压力非常大也非常忙,但收获也是满满的。在此特别来感谢楼上各位帮助过我的童鞋,谢谢你们!让我一个完全没学过数据库的小白拿到了offer

fieldmarshal : 2018-05-01#65
跟大家汇报一下

在星期六童鞋惊呼的天啦。。。又不肯提供帮助的情况下,我硬着头皮把作业交了上去。很快面试官就打来电话说非常满意并约好了下次面试的时间。前后一共三次技术面一次行为面一次笔试,终于拿到了offer。

昨天考完了最后一门课的期末考,结束了大学四年的全部考试。这段时间压力非常大也非常忙,但收获也是满满的。在此特别来感谢楼上各位帮助过我的童鞋,谢谢你们!让我一个完全没学过数据库的小白拿到了offer
恭喜你同时想说他眼瞎啊,:)

cetaphil : 2018-05-02#66
恭喜你同时想说他眼瞎啊,:)
可能是我的美貌亮瞎了他的眼。

fieldmarshal : 2018-05-02#67
可能是我的美貌亮瞎了他的眼。
这个makes sense。传说中二面的哥们看到一面哥们留言:让你看看啥是美女,:)

zhizhi123 : 2018-05-04#68
恭喜恭喜, 面试 官也知道 ,学习 sql 的语法给点时间 就能够, 但是 思路 不容易培养。
你自己 思路是清晰的 这是他们要你的主要原因, 只是不熟 sql 而已。
而且你也看到了 这么多 我们这些 干了 这么多年的人 也不咋地。
本身 数学 就比计算机难。 能学数学的 肯定 学计算机 so easy.

printf : 2018-05-04#69
数据库,难的是建库!

看关系表就知道了。

smilebabyface : 2018-05-04#70
跟大家汇报一下

在星期六童鞋惊呼的天啦。。。又不肯提供帮助的情况下,我硬着头皮把作业交了上去。很快面试官就打来电话说非常满意并约好了下次面试的时间。前后一共三次技术面一次行为面一次笔试,终于拿到了offer。

昨天考完了最后一门课的期末考,结束了大学四年的全部考试。这段时间压力非常大也非常忙,但收获也是满满的。在此特别来感谢楼上各位帮助过我的童鞋,谢谢你们!让我一个完全没学过数据库的小白拿到了offer
恭喜,精神可嘉

cetaphil : 2018-05-04#71
恭喜恭喜, 面试 官也知道 ,学习 sql 的语法给点时间 就能够, 但是 思路 不容易培养。
你自己 思路是清晰的 这是他们要你的主要原因, 只是不熟 sql 而已。
而且你也看到了 这么多 我们这些 干了 这么多年的人 也不咋地。
本身 数学 就比计算机难。 能学数学的 肯定 学计算机 so easy.
谢谢鼓励!您说的没错,我确实需要多多学习数据库知识,毕竟对full stack developer来说也是必须的技能。家园网卧虎藏龙,以后碰到各种各样技术问题还是要向你们求助呀!

fieldmarshal : 2018-05-04#72
谢谢鼓励!您说的没错,我确实需要多多学习数据库知识,毕竟对full stack developer来说也是必须的技能。家园网卧虎藏龙,以后碰到各种各样技术问题还是要向你们求助呀!
我觉得你有两个出路,一个找个写程序厉害的老公,另外是改行,:)

cetaphil : 2018-05-04#73
我觉得你有两个出路,一个找个写程序厉害的老公,另外是改行,:)
我需要指点,但不需要指指点点。下次回帖请把那个贱贱的表情去掉。

agent1234 : 2018-05-28#74
  • Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.
这是在招dba吗?最讨厌这种database specific的问题,作为developer需要了解那么多吗

cetaphil : 2018-05-28#75
  • Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.
这是在招dba吗?最讨厌这种database specific的问题,作为developer需要了解那么多吗
不是

fieldmarshal : 2018-05-29#76
  • Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.
这是在招dba吗?最讨厌这种database specific的问题,作为developer需要了解那么多吗
sql只是个实现方式,解决问题思路是相通的