将MSSQL表传输到内存中没有时间戳列的OLTP技术

时间:2017-07-04 20:14:43

标签: sql-server tsql sql-server-2016

由于MS SQL Server Express 2016免费提供新的内存中OLTP,我想将表格转移到新技术中。这样做,我有一个问题,因为我正在使用时间戳列来找出一个人的最新数据集 - 例如最新的电子邮件地址。

为了演示这个问题,你需要生成3个表。第一个人:

CREATE TABLE [dbo].[persons](
[Pers_ID] [int] IDENTITY(1,1) NOT NULL,
[Pers_surname] [nvarchar](100) NULL,
[Pers_forename] [nvarchar](100) NULL,
[Pers_birthdate] [datetime] NULL,
[Pers_last_change] [datetime] NULL,
[Pers_changed_by] [nvarchar](100) NULL,
[Pers_Timestamp] [timestamp] NULL,
CONSTRAINT [PK_persons] PRIMARY KEY CLUSTERED 
([Pers_ID] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,     
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, 
FILLFACTOR = 90) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

电子邮件地址的第二个:

CREATE TABLE [dbo].[emailadress](
[Email_ID] [int] IDENTITY(1,1) NOT NULL,
[Email_adress] [nvarchar](255) NULL,
[Email_up_to_date] [bit] NOT NULL,
[Email_last_change] [datetime] NULL,
[Email_changed_by] [nvarchar](100) NULL,
[Email_Timestamp] [timestamp] NULL,
CONSTRAINT [PK_emailadress] PRIMARY KEY CLUSTERED 
([Email_ID] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, 
FILLFACTOR = 90) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [dbo].[emailadress] ADD  CONSTRAINT 
[DF_emailadress_up_to_date]  DEFAULT ((1)) FOR [Email_up_to_date]
GO

两个第一个表之间链接的最后一个:

CREATE TABLE [dbo].[PersEmail](
[PersEmail_ID] [int] IDENTITY(1,1) NOT NULL,
[Pers_ID] [int] NULL,
[Email_ID] [int] NULL,
[PersEmail_out_of_date] [bit] NOT NULL,
[PersEmail_last_change] [datetime] NULL,
[PersEmail_changed_by] [nvarchar](255) NULL,
[PersEmail_Timestamp] [timestamp] NULL,
CONSTRAINT [PK_PersEmail] PRIMARY KEY CLUSTERED 
([PersEmail_ID] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, 
FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY] 
GO

每个人都可以有几个电子邮件地址,一个电子邮件地址可以链接到几个人。如果电子邮件地址已过期,则会有一个触发器将连接设置为过期。

对于很多报道,我只需要一个人的电子邮件地址。这应该是最新的电子邮件地址。要找出哪个是最新的,我使用timestamp列:

SELECT P.Pers_ID, Pers_surname, Pers_forename, Pers_birthdate, 
PersEmail_ID, Email_adress

FROM persons P

LEFT OUTER JOIN
(SELECT * FROM dbo.PersEmail WHERE PersEmail_out_of_date = 0) AS PE 
ON PE.Pers_ID = P.Pers_ID 

LEFT OUTER JOIN
(SELECT * FROM dbo.emailadress WHERE Email_up_to_date = 1) AS email 
ON email.Email_ID = PE.Email_ID

WHERE P.Pers_ID = @intID AND

((PE.PersEmail_Timestamp IS NULL) OR
(PE.PersEmail_Timestamp = ( SELECT MAX(PersEmail_Timestamp) AS Expr1
FROM (SELECT * FROM dbo.PersEmail WHERE PersEmail_out_of_date = 0) AS 
persemail2
WHERE       Pers_ID = P.Pers_ID)))

RETURN

我尝试使用datetime2(7)列而不是timestamp列。但是,该人可能会立即使用“更新”声明更改两个电子邮件地址。 datetime2列现在将包含两个相同的日期时间。然后我的查询将显示两行而不是一行。相比之下,timestamp列提供了两个不同的内容,我的查询只提供了一行。

有没有人知道如何替换描述目标的时间戳列?

如果存在独立于In-Memory-Question的建议,以改善查询以找出最新的电子邮件地址,请告诉我。

注意:我希望上面引用的代码是正确的。我从德语翻译了列名,并尝试使用英文版本。原始代码可以正常工作......如果我忽略了错误,请告诉我。

最后这段代码似乎对我有用,但我不确定,这是最好的:

SELECT      P.Pers_ID, Pers_surname, Pers_forename, Pers_birthdate, 
PE.PersEmail_ID, Email_adress

FROM        persons P

LEFT OUTER JOIN
(SELECT * FROM PersEmail WHERE PersEmail_out_of_date = 0) AS PE 
ON  PE.Pers_ID = P.Pers_ID 

LEFT OUTER JOIN
(SELECT * FROM emailadress WHERE Email_up_to_date = 1) AS email ON 
email.Email_ID = PE.Email_ID

WHERE       P.Pers_ID = @intID AND

((PE.PersEmail_TS IS NULL) OR
(PE.PersEmail_ID =  (   SELECT TOP 1    PersEmail_ID
FROM        (SELECT * FROM dbo.PersEmail WHERE PersEmail_out_of_date = 0) 
AS persemail3       
WHERE       Pers_ID = @intID

AND PersEmail_TS = (    SELECT      MAX(PersEmail_TS) AS Expr1
FROM        (SELECT * FROM dbo.PersEmail WHERE PersEmail_out_of_date = 0)  
AS persemail2
WHERE       Pers_ID = @intID))))

2 个答案:

答案 0 :(得分:1)

如果您在一个UPDATE语句中更新同一个人的两个电子邮件地址,则无法保证这些电子邮件地址中的哪一个将获得最高时间戳。 @Jeroen Mostert在评论中也提到了这一点。

当你转移到内存中的OLTP(不支持时间戳数据类型)时,使用datetime2(7)列应该没问题。您必须对查询进行微小更改,以确保不会收到多行。

查询重写的一个建议是:

SELECT TOP 1
    P.Pers_ID, Pers_surname, Pers_forename, Pers_birthdate, PersEmail_ID, Email_adress
FROM persons P
    LEFT OUTER JOIN dbo.PersEmail PE 
        ON PE.Pers_ID = P.Pers_ID 
        AND PE.PersEmail_inaktuell = 0
    LEFT OUTER JOIN dbo.emailadress email 
        ON email.Email_ID = PE.Email_ID 
        AND email.Email_up_to_date = 1
WHERE P.Pers_ID = @intID 
ORDER BY PE.PersEmail_Timestamp DESC, PE.Email_ID DESC

此查询仍然确保您只收到1条记录,并且收到的记录是最新更新的记录之一。即如果您的PersEmail表中的两个或多个记录在同一个UPDATE语句中更新,那么您将收到具有最高Email_ID的记录。

答案 1 :(得分:0)

这段代码终于为我工作了。感谢“TOP 1”的提示:

SELECT      P.Pers_ID, PE.PersEmail_ID, PE.Email_ID, Email_Adress

FROM        persons P

            INNER JOIN
            (SELECT * FROM PersEmail WHERE PersEmail_out_of_date = 0) AS PE ON  PE.Pers_ID = P.Pers_ID 

            INNER JOIN
            (   SELECT      * 
                FROM        emailadress
                WHERE       Email_up_to_date = 1
            )   AS E ON E.Email_ID = PE.Email_ID

  WHERE     (P.Pers_ID = @intID OR @intID=0)

        AND

            ((PE.PersEmail_TS IS NULL) OR
            (PE.PersEmail_ID =  (   SELECT TOP 1    PersEmail_ID
                                    FROM        (SELECT * FROM dbo.PersEmail WHERE PersEmail_inaktuell = 0) AS persemail3       
                                    WHERE       Pers_ID = P.Pers_ID
                                            AND PersEmail_TS = (    SELECT      MAX(PersEmail_TS) AS Expr1
                                                                    FROM        (SELECT * FROM dbo.PersEmail WHERE PersEmail_out_of_date = 0) AS persemail2
                                                                    WHERE       Pers_ID = P.Pers_ID))))