我正在使用的数据
请考虑以下两个数据库表:
CREATE TABLE [dbo].[Contact](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Contact_UID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Contact_Contact_UID] DEFAULT (newsequentialid()),
[Name] [nvarchar](255) NOT NULL,
[ContactStatus] [nvarchar](255) NOT NULL)
CREATE TABLE [dbo].[Contact_Log](
[ID] [int] IDENTITY(1,1) NOT NULL,
[LogDate] [datetimeoffset](7) NOT NULL CONSTRAINT [DF_Contact_Log_LogDate] DEFAULT (sysdatetimeoffset()),
[Contact_UID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Contact_Log_Contact_UID] DEFAULT (newsequentialid()),
[Name] [nvarchar](255) NOT NULL,
[ContactStatus] [nvarchar](255) NOT NULL)
联系人表是联系人记录的主表。它存储联系人的姓名和状态(例如“活着”,“死亡”或其他)。
Contact_Log表存储对Contact表所做的所有更改。
所以这是一些示例数据:
联系人:
+----+--------------------------------------+------+---------------+
| ID | Contact_UID | Name | ContactStatus |
+----+--------------------------------------+------+---------------+
| 1 | 62918AC1-1C6C-4DEB-B7F8-5D5EF913F667 | John | Dead |
+----+--------------------------------------+------+---------------+
| 2 | F7844037-2FF5-47B9-874D-C0920E7DC092 | Jane | Alive |
+----+--------------------------------------+------+---------------+
Contact_Log:
+----+--------------------------------------+------+---------------+------------+
| ID | Contact_UID | Name | ContactStatus | LogDate |
+----+--------------------------------------+------+---------------+------------+
| 1 | 62918AC1-1C6C-4DEB-B7F8-5D5EF913F667 | John | Alive | 2019-01-01 |
+----+--------------------------------------+------+---------------+------------+
| 2 | 62918AC1-1C6C-4DEB-B7F8-5D5EF913F667 | John | Dead | 2019-01-02 |
+----+--------------------------------------+------+---------------+------------+
| 3 | 62918AC1-1C6C-4DEB-B7F8-5D5EF913F667 | John | Dead | 2019-01-03 |
+----+--------------------------------------+------+---------------+------------+
| 4 | F7844037-2FF5-47B9-874D-C0920E7DC092 | Jane | Alive | 2019-01-04 |
+----+--------------------------------------+------+---------------+------------+
注意:至此,我还没有在该表上添加任何索引或类似的内容。
测试场景
以上只是一些示例数据。我正在测试的数据具有以下行计数:
联系人:〜10,000行
Contact_Log:〜3,000,000行
我目前正在使用SQL Server 2008 R2进行测试。因此,首选在该版本及更高版本中受支持的解决方案。
我想要达到的目标
[基本上,我正在尝试制定一个查询,该查询可以告诉我LogDate
字段上一次更改的时间,对于特定的ContactStatus
,取自Contact_UID
表。
例如,如果我感兴趣的记录是“ John”,那么结果应该是“ 2019-01-02”。因为这是John的ContactStatus上次更改的日期(即,它从“ Alive”更改为“ Dead”)。
最终,我想将此查询放入函数中。可以通过传入Contact_UID和我要检查的字段名称来调用的函数。然后可以将此函数作为更通用查询的一部分来调用。例如:
Contact_Log
到目前为止我尝试过的事情
嗯,我已经尝试了一些方法,尽管我可以获得想要的结果。我的尝试确实遇到了性能问题。
注意:尽管我真的只想要一个datetimeoffset结果。有些尝试只是为了验证数据的准确性而包括更多的数据/字段。
尝试1:
SELECT Name, MyFunction('62918AC1-1C6C-4DEB-B7F8-5D5EF913F667', 'ContactStatus') AS StatusLastChanged FROM Contact
问题1:太慢。等待了近一个小时而没有结果,我不得不停止查询。
尝试2:
SELECT TOP(1) a.LogDate
FROM Contact_Log AS a
WHERE a.Contact_UID = '62918AC1-1C6C-4DEB-B7F8-5D5EF913F667'
AND a.ContactStatus <>
(
SELECT TOP(1) b.ContactStatus
FROM Contact_Log AS b
WHERE b.Contact_UID = '62918AC1-1C6C-4DEB-B7F8-5D5EF913F667'
AND a.LogDate > b.LogDate
ORDER BY b.LogDate DESC
)
ORDER BY LogDate DESC
问题2:这有效,并且给了我正确的数据集。但是,这需要6秒钟,这太慢了。请记住,它将需要在更通用的查询(约10,000行)中作为函数使用。
尝试3:现在,这基本上与尝试2相同,希望我尝试应用SELECT A.LogDate
FROM (SELECT ROW_NUMBER() OVER (ORDER BY LogDate DESC, ID DESC) AS rnum, ID, LogDate, Contact_UID, ContactStatus FROM Contact_Log) A
LEFT JOIN (SELECT ROW_NUMBER() OVER (ORDER BY LogDate DESC, ID DESC) AS rnum, ID, LogDate, Contact_UID, ContactStatus FROM Contact_Log) B
ON A.rnum = B.rnum-1
WHERE
(B.rnum IS NULL
OR (A.Contact_UID = '62918AC1-1C6C-4DEB-B7F8-5D5EF913F667'
AND B.Contact_UID = '62918AC1-1C6C-4DEB-B7F8-5D5EF913F667'
AND A.ContactStatus != B.ContactStatus))
ORDER BY A.rnum
,以便可以得到我想要的结果。
TOP(1)
问题3:令我惊讶的是,这比尝试2花费的时间长得多,尽管我所做的只是在开始时添加SELECT TOP(1) A.LogDate
FROM (SELECT ROW_NUMBER() OVER (ORDER BY LogDate DESC, ID DESC) AS rnum, ID, LogDate, Contact_UID, ContactStatus FROM Contact_Log) A
LEFT JOIN (SELECT ROW_NUMBER() OVER (ORDER BY LogDate DESC, ID DESC) AS rnum, ID, LogDate, Contact_UID, ContactStatus FROM Contact_Log) B
ON A.rnum = B.rnum-1
WHERE
(B.rnum IS NULL
OR (A.Contact_UID = '62918AC1-1C6C-4DEB-B7F8-5D5EF913F667'
AND B.Contact_UID = '62918AC1-1C6C-4DEB-B7F8-5D5EF913F667'
AND A.ContactStatus != B.ContactStatus))
ORDER BY A.rnum
。这花了5分钟多的时间,所以我停止了查询并放弃了。
问题
我如何在“我要实现的目标”中做我想做的,但要有合理的表现? (在此阶段,我很乐意将其控制在1秒以内)。
记住,我只想要一个datetimeoffset作为结果,以便可以在函数中使用。
到目前为止,我还没有创建特定的索引。如果无法改善查询条件,我很乐意为您提供建议。或对架构进行适当的更改。
底线
我正在寻找一个查询,该查询将产生1个结果,带有1个datetimeoffset字段。它只需不到1秒的时间即可运行。
您想在最高日期之后选择不等于当前ContactStatus的最小日期。就像这样:
TOP(1)