将 MSSQL table 传输到没有时间戳列的 In-Memory OLTP 技术

Transfer MSSQL table to In-Memory OLTP technique without timestamp column

由于 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

前两个表之间link的最后一个:

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

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

对于很多报告,我只需要一个人的电子邮件地址。这应该是最新的电子邮件地址。为了找出最新的,我使用时间戳列:

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) 列而不是时间戳列。但是,此人有可能同时使用 "Update" 语句更改了两个电子邮件地址。 datetime2 列现在将包含两个相同的日期时间。然后我的查询将显示两行而不是一行。相比之下,时间戳列提供了两种不同的内容,而我的查询只提供了一行。

有谁知道如何为所描述的目标替换时间戳列?

如果有独立于内存问题的建议来改进查询以找出最新的电子邮件地址,也请告诉我。

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

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

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))))

如果您在一个 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 条记录,并且收到的记录是最新更新的记录之一。 IE。如果您的 PersEmail table 中的两条或更多条记录在同一 UPDATE 语句中被更新,那么您将收到具有最高 Email_ID.

的一条

这段代码终于对我有用了。感谢 "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))))