首先,看了一遍题目,我的感觉这是个开放性的问题,也就是说并不一定有统一标准答案。
其次,题目的大概意思是每一次通过时间戳抓取的都是最新更新的数据,排除掉以前变化的数据。
最后,找了一遍目录,没找到这个PIT functions are generated automatically,估计就把这个PIT function写一下就行了,
PIT function也很简单就是抓取point in time数据,就是根据时间戳到Audit表里fetch一些数据就可以了。
至于其他的improvement, solution, suggestion就根据你学过的数据库知识对照这个题目套用一下,稍微延伸一下就可以了。
大神好,trigger已经写好了,分别是delete,update和insert,现在的问题就是怎么写这个function dbo.data1_PIT,完全没写过function不知道怎么下手。。用triggers来写,你的题目第三条已经要求了, 网上找一下怎么写这属于t-sql
Creat trigger xxx on xxx
After insert, update, delete
As
If .....
Insert into xxx1 ()
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
我是数学专业本科生,辅修了一些计算机课程,没上过数据库这门课,对sql的了解仅限SELECT FROM。。。本来一面的时候问的都是一些基础数据结构Java Javascript Nodejs等等,哪里知道二面的面试官抓着我问SQL,上来就是trigger,完全没学过。他本来要拒绝我了,看在一面面试官极力推荐的份上给我留个题看能不能解出来。。。说实话 这个挺难的 像是 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,
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
这个很容易!这题都做不了的选手在公司是个废人说实话 这个挺难的 像是 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,
这么容易,你给把那个pit 的 function 写一下 贴上来呗。 吹牛胡说 谁不会 啊。这个很容易!这题都做不了的选手在公司是个废人
吹牛不动脑轻松你不知道么?哈这么容易,你给把那个pit 的 function 写一下 贴上来呗。 吹牛胡说 谁不会 啊。
啊呸 ,吹牛不动脑轻松你不知道么?哈
不过真的,我是告诉你业界的标准在那里,你要是不信随便你,呵呵
网上很多公开的面经,你看看大公司稍微有点难度的题目!你还可以找几个面试是开卷考试的公司做做
这题做好了应该可以拿到offer如果公司要求不苛刻,但这个难度一般不是压轴题
好像你都已经做完了,难道你的问题是数据库里的表的函数,是怎么定义的,写在哪里,怎么被调用吗?我感觉你的问题是不太明白ddl(数据定义语言),与dml(数据操纵语言)的写在哪里,怎么被调用,他们的运行机制的问题。我是数学专业本科生,辅修了一些计算机课程,没上过数据库这门课,对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、
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
大神,可否指导我一下怎么写这个function() ?抓耳挠腮想了半天还是没写出来,下周一还有两个final,现在重新啃sql syntax也来不及了。这个很容易!这题都做不了的选手在公司是个废人
说实话 这个挺难的 像是 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,
嗯嗯我已经基本上写出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。应该不用倒叙那么复杂,一个复杂查询就行了。
查询audit表的条件:
- 记录时间小于等于函数参数指定的时间
- 每个主键只取时间最新的那条记录
- 记录的状态不能是删除
主键在这里特指主表里面绝对不会被修改、而且唯一的字段。
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
嘿,你好像是回答的最靠谱的一个人。你说的对。应该不用倒叙那么复杂,一个复杂查询就行了。
查询audit表的条件:
- 记录时间小于等于函数参数指定的时间
- 每个主键值只取时间最新的那条记录
- 记录的状态不能是删除
主键在这里特指主表里面绝对不会被修改、而且唯一的字段。
没看明白Function要做什么。嗯嗯我已经基本上写出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
你想太多了。面试官只是随便用个常用场景考一下而已。—— 因为数据库每时每刻都在改变和更新所以在不同时间抓出来的信息不一样。
这个设计思想,使得服务器负担比较重。
如果结合事务日程和日志的话好像没必要。
这题目有点象 Rtos 的设计思想?
意思就是说每次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。。没看明白Function要做什么。
两个tables没有 join 一下? group by [date], [id_security] order by [change_date] ?
我也是菜鸟,
谢谢美女姐姐!我看了一下您的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).首先指出我和楼主对题目理解不同的几点:
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可能会更难一些。祝楼主好运。
---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')
![]()
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
谢谢美女姐姐!我看了一下您的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
我之前附的这张图就是原题,trigger面试官已经写好了,现在只剩implement function。我不大明白你的意思。题目要求是有提供changedate 的。
你的题目:
In the future, even though records can be updated or deleted, if
你的题目的第一点提供了2个时间点。这个是原题吗?还是你自己的理解?如果是你自己的理解而不是原题的话,建议你把原本的题目贴出来看一下。
- 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')
我不大明白你的意思。题目要求是有提供changedate 的。
你的题目:
In the future, even though records can be updated or deleted, if
你的题目的第一点 “dbo.data1_PIT('2011-12-31', '2018-02-22 14:42:22')”提供了2个时间点, 第二个应该是 changedate, 所以面试官应该是是提供了changedate 的。这个是原题吗?还是你自己的理解?如果是你自己的理解而不是原题的话,建议你把原本的题目贴出来看一下。
- 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')
你理解非常错误,楼主理解是正确的,亏你是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 .
是这样的,假设当前时刻 是 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
美女姐姐别生气,我说的有些绕,xiaoa表达的比我清楚。您的思路是对的,只是跟面试官的要求不太一致。面试官后来还加了一句hint:我是按英文这部分来理解给的建议。我的理解可能是错的,不过我也只是好心给出我的看法而已。我们做project 的话都有详细的project doc, 所以不存在理解有分歧的情况。因为如果什么理解有歧义,在写code 之前就会找 stakeholder 文明白了,呵呵。我是不是很理解楼主的题目,所以不好意思帮不到楼主。之前楼主说不会写function的 syntax, 我的例子中至少syntax 是对的,楼主可以按你的理解改一下来用吧。
面试题是:Create SQL objects to return Point-in-time data that generate past results
To be more clear
- Create SQL object to show data in the same structure as dbo.data1 but as of certain date/time.
- Now time is 2018-02-22 14:42:22.
- I do SELECT * FROM dbo.data1 WHERE date = '2011-12-31'
- and get some records.
- 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')
- 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.
我自己都6年 full stack developer , 而且自认作数据库还可以,都没做对。是因为长得漂亮的人写Code都很糟糕吗?呵呵。我写Code也只有两年,初级水平,还在努力进步中,见笑了。
首先指出我和楼主对题目理解不同的几点:
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可能会更难一些。祝楼主好运。
---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 里面的逻辑。Mimi写的第二个parameter changeDate是数据更改并记录进audit的时间,然而面试官要求的parameter是上次进行select操作的时间
然鹅我不会写所以你要改她的那个 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
大爷,我就这一招。 你行你up 给我们看看 怎么搭讪。和美女搭讪也不能光靠贬低自己水平……
您写的changedate<=@ChangeDate会把这个row自创建以来一直到@ChangeDate的所有update/delete的log都return出来的,根据要求,我们只想return出一条row。而且应该是在@ChangeDate之后的第一条row。原因是无论是delete还是update存进audit table都是被删除的row。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表达的比我清楚。您的思路是对的,只是跟面试官的要求不太一致。面试官后来还加了一句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 doc, 所以不存在理解有分歧的情况。因为如果什么理解有歧义,在写code 之前就会找 stakeholder 问明白了。呵呵。我
不是很理解楼主的题目,所以不好意思帮不到楼主。之前楼主说不会写function的 syntax, 我的例子中至少syntax 是对的,楼主可以按你的理解改一下来用吧。
面试题是:Create SQL objects to return Point-in-time data that generate past results
To be more clear
- Create SQL object to show data in the same structure as dbo.data1 but as of certain date/time.
- Now time is 2018-02-22 14:42:22.
- I do SELECT * FROM dbo.data1 WHERE date = '2011-12-31'
- and get some records.
- 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')
- 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.
你谦虚。软件行业是年轻人的天下。大叔也是Developer哦,真巧。我经验还少,连senior 都没做上呢。希望是我向你请教才是,呵呵。
感谢,前两天忙着期末考没时间写,我照着您的代码和楼上各位数据库大神的指导差不多写出来了。感觉目前回答的思路好像都不大对,简单说说我的想法:
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
)
这sql……天啦......感谢,前两天忙着期末考没时间写,我照着您的代码和楼上各位数据库大神的指导差不多写出来了。
代码: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 )
恭喜你同时想说他眼瞎啊,:)跟大家汇报一下
在星期六童鞋惊呼的天啦。。。又不肯提供帮助的情况下,我硬着头皮把作业交了上去。很快面试官就打来电话说非常满意并约好了下次面试的时间。前后一共三次技术面一次行为面一次笔试,终于拿到了offer。
昨天考完了最后一门课的期末考,结束了大学四年的全部考试。这段时间压力非常大也非常忙,但收获也是满满的。在此特别来感谢楼上各位帮助过我的童鞋,谢谢你们!让我一个完全没学过数据库的小白拿到了offer
这个makes sense。传说中二面的哥们看到一面哥们留言:让你看看啥是美女,:)可能是我的美貌亮瞎了他的眼。
恭喜,精神可嘉跟大家汇报一下
在星期六童鞋惊呼的天啦。。。又不肯提供帮助的情况下,我硬着头皮把作业交了上去。很快面试官就打来电话说非常满意并约好了下次面试的时间。前后一共三次技术面一次行为面一次笔试,终于拿到了offer。
昨天考完了最后一门课的期末考,结束了大学四年的全部考试。这段时间压力非常大也非常忙,但收获也是满满的。在此特别来感谢楼上各位帮助过我的童鞋,谢谢你们!让我一个完全没学过数据库的小白拿到了offer
谢谢鼓励!您说的没错,我确实需要多多学习数据库知识,毕竟对full stack developer来说也是必须的技能。家园网卧虎藏龙,以后碰到各种各样技术问题还是要向你们求助呀!恭喜恭喜, 面试 官也知道 ,学习 sql 的语法给点时间 就能够, 但是 思路 不容易培养。
你自己 思路是清晰的 这是他们要你的主要原因, 只是不熟 sql 而已。
而且你也看到了 这么多 我们这些 干了 这么多年的人 也不咋地。
本身 数学 就比计算机难。 能学数学的 肯定 学计算机 so easy.
我觉得你有两个出路,一个找个写程序厉害的老公,另外是改行,:)谢谢鼓励!您说的没错,我确实需要多多学习数据库知识,毕竟对full stack developer来说也是必须的技能。家园网卧虎藏龙,以后碰到各种各样技术问题还是要向你们求助呀!
我需要指点,但不需要指指点点。下次回帖请把那个贱贱的表情去掉。我觉得你有两个出路,一个找个写程序厉害的老公,另外是改行,:)
不是这是在招dba吗?最讨厌这种database specific的问题,作为developer需要了解那么多吗
- Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.
sql只是个实现方式,解决问题思路是相通的这是在招dba吗?最讨厌这种database specific的问题,作为developer需要了解那么多吗
- Is SQL 2008/12/16 built in audit better solution? We audit just selected tables.