Sql 数据库中的临时外键
Temporal Foreign Key in Sql Database
link 表在确定的时间段内具有唯一关系的最佳做法是什么?
示例 1
Tab_Civil_Status:
ID
NAME
ID_STATUS
1
Jenny
1
2
Mike
2
Tab_Civil_Status_Desc:
ID_STATUS
STATUS_DESC
1
Single
2
Married
Tab_Civil_Events:
PERSON-ID
EVENT
REF_Date
new-Status
2
Wedding
10/5/2017
2
对 Tab_Civil_Status
的查询应提供以下内容:
案例A:
Select * FROM `Tab_Civil_Status` […] Where REF_Date = 1/1/2020
ID
NAME
ID_STATUS
1
Jenny
1
2
Mike
2
案例 B:
Select * FROM `Tab_Civil_Status` […] Where REF_Date = 1/1/2016
ID
NAME
ID_STATUS
1
Jenny
1
2
Mike
1
示例 2
Tab_Orders:
ID
Order_Code
Product_Id
1
a
1
2
b
2
Tab_Products:
Product_Id
Product_Name
1
Apple
2
Meta
Tab_Products_事件:
Product_Id
EVENT
REF_Date
Old_Name
New_Name
2
Change_Name
1/12/2020
Facebook
Meta
这些是简化的示例,我正在寻找的是针对类似情况构建表和查询的最佳实践;要应用的逻辑而不是要使用的实际代码。
a - 最好的做法是在上述情况下跟踪更改的“事件 Table”吗?或者有更好的方法?
我知道可能有几种方法可以达到相同的范围,下面是另一个示例
Tab_Products_Events_Sample_2
:
Product_Id
EVENT
REF_Date
Name
2
Given _Name
1/12/2010
Facebook
2
Change_Name
1/12/2019
Meta
b - 有比下面更有效的查询吗?同样基于以上观点
@Selected_date = '1/1/2019'
SELECT
o.[Order_Code],
ifnull(e.[Old_Name],p.[Product_Name]) as p_name
FROM Tab_Orders as o
LEFT JOIN Tab_Products as p
ON o.[Product_Id] = p.[Product_Id]
CROSS APPLY (
SELECT top 1 t.[Old_Name]
FROM Tab_Products_Events as t
WHERE o.[Product_Id] = t.[Product_Id]
and t.[REF_Date] >= @Selected_date
ORDER BY t.[REF_Date] desc
) e
结果
Order_Code
p_name
a
Apple
b
Facebook
谢谢
注意:我的回答是基于第一个例子
第一种方法:生成日期范围
您应该首先使用类似的查询提取每个人员状态的日期范围:
SELECT
PERSON_ID, new_Status, REF_Date,
ISNULL(LEAD(REF_Date) OVER(PARTITION BY PERSON_ID ORDER BY REF_DATE), GETDATE()) End_DATE
FROM
Tab_Civil_Events
例如,考虑以下示例数据:
CREATE TABLE Tab_Civil_Events
(
PERSON_ID INT,
EVENT VARCHAR(50),
REF_Date DATETIME,
new_Status INT
)
INSERT INTO Tab_Civil_Events(PERSON_ID, EVENT, REF_Date, new_Status)
VALUES (2, 'Wedding', '10/5/2017', 2),
(2, 'Divorce', '10/5/2018', 1),
(2, 'Wedding', '10/5/2019', 2),
(2, 'Divorce', '10/5/2020', 1)
SELECT
PERSON_ID, new_Status, REF_Date,
ISNULL(LEAD(REF_Date) OVER (PARTITION BY PERSON_ID ORDER BY REF_DATE),
GETDATE()) End_DATE
FROM
Tab_Civil_Events
这将导致以下 table:
PERSON_ID
new_Status
REF_Date
End_DATE
2
2
2017-10-05T00:00:00Z
2018-10-05T00:00:00Z
2
1
2018-10-05T00:00:00Z
2019-10-05T00:00:00Z
2
2
2019-10-05T00:00:00Z
2020-10-05T00:00:00Z
2
1
2020-10-05T00:00:00Z
2022-01-29T09:47:52.7Z
第二步是创建一个通用的 table 表达式来查询数据。例如:
DECLARE @QueryDate DATETIME = '20190501'
;WITH CTE_1 AS
(
SELECT
PERSON_ID,
new_Status,
REF_Date,
ISNULL(LEAD(REF_Date) OVER (PARTITION BY PERSON_ID ORDER BY REF_DATE),
GETDATE()) End_DATE
FROM
Tab_Civil_Events
)
SELECT
CTE_1.PERSON_ID,
dsc.STATUS
FROM
CTE_1
INNER JOIN
Tab_Civil_Status_Desc dsc ON CTE_1.new_Status = dsc.ID
WHERE
@QueryDate BETWEEN REF_Date AND End_DATE
使用我之前提供的示例数据,此查询将给出以下结果:
PERSON_ID
STATUS
2
Single
将日期值更改为 20191101
SET @QueryDate = '20191101'
将产生以下值:
PERSON_ID
STATUS
2
Married
确保您已经创建了适当的索引来支持您的查询。
例如:
CREATE NONCLUSTERED INDEX IX_PersonId_RefDate
ON Tab_Civil_Events(PERSON_ID, REF_DATE)
INCLUDE(new_status);
第二种方法:检索最近的事件日期
您应该首先检索值小于给定日期的每个人的最大事件日期:
SELECT
PERSON_ID,
MAX(Ref_Date) AS event_date
FROM
Tab_Civil_Events
WHERE
Ref_Date <= @QueryDate
GROUP BY
PERSON_ID
接下来,您应该将此查询与事件 table 连接起来,以获取如下状态信息:
DECLARE @QueryDate DATETIME = '20190501'
SELECT
tbl.PERSON_ID, dsc.STATUS
FROM
Tab_Civil_Events tbl
INNER JOIN
Tab_Civil_Status_Desc dsc ON tbl.new_Status = dsc.ID
INNER JOIN
(SELECT
PERSON_ID,
MAX(Ref_Date) AS event_date
FROM
Tab_Civil_Events
WHERE
Ref_Date <= @QueryDate
GROUP BY
PERSON_ID) t1 ON tbl.PERSON_ID = t1.PERSON_ID
AND t1.event_date = tbl.ref_date
第三种方法:时间 tables
在您提到您可以更改数据存储方式后,我发布了这一部分。
使用 SQL Server 2016 或更新版本,您可以使用时态 tables 来简化数据查询。
SQL Server 2016 introduced support for temporal tables (also known as system-versioned temporal tables) as a database feature that brings built-in support for providing information about data stored in the table at any point in time rather than only the data that is correct at the current moment in time.
时间 table 被实现为一对 table,当前 table 和历史 table。在这些 table 中的每一个中,开始和结束日期都被存储并用于定义每一行的有效期。
您可以参考以下资源了解更多关于此类数据结构的信息:
- Official documentation
- First Look at System-Versioned Temporal Tables-Part 1: Creating Tables and Modifying Data
- First Look at System-Versioned Temporal Tables-Part 2: Querying Data and Optimization Considerations
- Tips Concerning Temporal Tables
- Temporal Tables, Partitioning, and ColumnStore Indexes
link 表在确定的时间段内具有唯一关系的最佳做法是什么?
示例 1
Tab_Civil_Status:
ID | NAME | ID_STATUS |
---|---|---|
1 | Jenny | 1 |
2 | Mike | 2 |
Tab_Civil_Status_Desc:
ID_STATUS | STATUS_DESC |
---|---|
1 | Single |
2 | Married |
Tab_Civil_Events:
PERSON-ID | EVENT | REF_Date | new-Status |
---|---|---|---|
2 | Wedding | 10/5/2017 | 2 |
对 Tab_Civil_Status
的查询应提供以下内容:
案例A:
Select * FROM `Tab_Civil_Status` […] Where REF_Date = 1/1/2020
ID | NAME | ID_STATUS |
---|---|---|
1 | Jenny | 1 |
2 | Mike | 2 |
案例 B:
Select * FROM `Tab_Civil_Status` […] Where REF_Date = 1/1/2016
ID | NAME | ID_STATUS |
---|---|---|
1 | Jenny | 1 |
2 | Mike | 1 |
示例 2
Tab_Orders:
ID | Order_Code | Product_Id |
---|---|---|
1 | a | 1 |
2 | b | 2 |
Tab_Products:
Product_Id | Product_Name |
---|---|
1 | Apple |
2 | Meta |
Tab_Products_事件:
Product_Id | EVENT | REF_Date | Old_Name | New_Name |
---|---|---|---|---|
2 | Change_Name | 1/12/2020 | Meta |
这些是简化的示例,我正在寻找的是针对类似情况构建表和查询的最佳实践;要应用的逻辑而不是要使用的实际代码。
a - 最好的做法是在上述情况下跟踪更改的“事件 Table”吗?或者有更好的方法?
我知道可能有几种方法可以达到相同的范围,下面是另一个示例
Tab_Products_Events_Sample_2
:
Product_Id | EVENT | REF_Date | Name |
---|---|---|---|
2 | Given _Name | 1/12/2010 | |
2 | Change_Name | 1/12/2019 | Meta |
b - 有比下面更有效的查询吗?同样基于以上观点
@Selected_date = '1/1/2019'
SELECT
o.[Order_Code],
ifnull(e.[Old_Name],p.[Product_Name]) as p_name
FROM Tab_Orders as o
LEFT JOIN Tab_Products as p
ON o.[Product_Id] = p.[Product_Id]
CROSS APPLY (
SELECT top 1 t.[Old_Name]
FROM Tab_Products_Events as t
WHERE o.[Product_Id] = t.[Product_Id]
and t.[REF_Date] >= @Selected_date
ORDER BY t.[REF_Date] desc
) e
结果
Order_Code | p_name |
---|---|
a | Apple |
b |
谢谢
注意:我的回答是基于第一个例子
第一种方法:生成日期范围
您应该首先使用类似的查询提取每个人员状态的日期范围:
SELECT
PERSON_ID, new_Status, REF_Date,
ISNULL(LEAD(REF_Date) OVER(PARTITION BY PERSON_ID ORDER BY REF_DATE), GETDATE()) End_DATE
FROM
Tab_Civil_Events
例如,考虑以下示例数据:
CREATE TABLE Tab_Civil_Events
(
PERSON_ID INT,
EVENT VARCHAR(50),
REF_Date DATETIME,
new_Status INT
)
INSERT INTO Tab_Civil_Events(PERSON_ID, EVENT, REF_Date, new_Status)
VALUES (2, 'Wedding', '10/5/2017', 2),
(2, 'Divorce', '10/5/2018', 1),
(2, 'Wedding', '10/5/2019', 2),
(2, 'Divorce', '10/5/2020', 1)
SELECT
PERSON_ID, new_Status, REF_Date,
ISNULL(LEAD(REF_Date) OVER (PARTITION BY PERSON_ID ORDER BY REF_DATE),
GETDATE()) End_DATE
FROM
Tab_Civil_Events
这将导致以下 table:
PERSON_ID | new_Status | REF_Date | End_DATE |
---|---|---|---|
2 | 2 | 2017-10-05T00:00:00Z | 2018-10-05T00:00:00Z |
2 | 1 | 2018-10-05T00:00:00Z | 2019-10-05T00:00:00Z |
2 | 2 | 2019-10-05T00:00:00Z | 2020-10-05T00:00:00Z |
2 | 1 | 2020-10-05T00:00:00Z | 2022-01-29T09:47:52.7Z |
第二步是创建一个通用的 table 表达式来查询数据。例如:
DECLARE @QueryDate DATETIME = '20190501'
;WITH CTE_1 AS
(
SELECT
PERSON_ID,
new_Status,
REF_Date,
ISNULL(LEAD(REF_Date) OVER (PARTITION BY PERSON_ID ORDER BY REF_DATE),
GETDATE()) End_DATE
FROM
Tab_Civil_Events
)
SELECT
CTE_1.PERSON_ID,
dsc.STATUS
FROM
CTE_1
INNER JOIN
Tab_Civil_Status_Desc dsc ON CTE_1.new_Status = dsc.ID
WHERE
@QueryDate BETWEEN REF_Date AND End_DATE
使用我之前提供的示例数据,此查询将给出以下结果:
PERSON_ID | STATUS |
---|---|
2 | Single |
将日期值更改为 20191101
SET @QueryDate = '20191101'
将产生以下值:
PERSON_ID | STATUS |
---|---|
2 | Married |
确保您已经创建了适当的索引来支持您的查询。
例如:
CREATE NONCLUSTERED INDEX IX_PersonId_RefDate
ON Tab_Civil_Events(PERSON_ID, REF_DATE)
INCLUDE(new_status);
第二种方法:检索最近的事件日期
您应该首先检索值小于给定日期的每个人的最大事件日期:
SELECT
PERSON_ID,
MAX(Ref_Date) AS event_date
FROM
Tab_Civil_Events
WHERE
Ref_Date <= @QueryDate
GROUP BY
PERSON_ID
接下来,您应该将此查询与事件 table 连接起来,以获取如下状态信息:
DECLARE @QueryDate DATETIME = '20190501'
SELECT
tbl.PERSON_ID, dsc.STATUS
FROM
Tab_Civil_Events tbl
INNER JOIN
Tab_Civil_Status_Desc dsc ON tbl.new_Status = dsc.ID
INNER JOIN
(SELECT
PERSON_ID,
MAX(Ref_Date) AS event_date
FROM
Tab_Civil_Events
WHERE
Ref_Date <= @QueryDate
GROUP BY
PERSON_ID) t1 ON tbl.PERSON_ID = t1.PERSON_ID
AND t1.event_date = tbl.ref_date
第三种方法:时间 tables
在您提到您可以更改数据存储方式后,我发布了这一部分。
使用 SQL Server 2016 或更新版本,您可以使用时态 tables 来简化数据查询。
SQL Server 2016 introduced support for temporal tables (also known as system-versioned temporal tables) as a database feature that brings built-in support for providing information about data stored in the table at any point in time rather than only the data that is correct at the current moment in time.
时间 table 被实现为一对 table,当前 table 和历史 table。在这些 table 中的每一个中,开始和结束日期都被存储并用于定义每一行的有效期。
您可以参考以下资源了解更多关于此类数据结构的信息:
- Official documentation
- First Look at System-Versioned Temporal Tables-Part 1: Creating Tables and Modifying Data
- First Look at System-Versioned Temporal Tables-Part 2: Querying Data and Optimization Considerations
- Tips Concerning Temporal Tables
- Temporal Tables, Partitioning, and ColumnStore Indexes