【问题标题】:SQL Server: table design for changing Identity recordsSQL Server:用于更改身份记录的表设计
【发布时间】:2016-09-26 00:30:21
【问题描述】:

我有一个视图,它显示来自多个来源的 GPS 点的测试数据。 该视图显示“GPS 点 ID”以及与该 GPS 点相关的一些地质测试结果。

GPS-POINT-ID 是这样的:XYZ-0XX-CCCCC

  • XYZ:区域
  • 00XX:身份证
  • CCCC:坐标

GPS点名随时间变化,点名前半部分(XYZ-0XX)不变,但坐标部分(CCCC)根据新的GPS点位置变化。

我想设计一个将前面提到的视图作为数据源的表。我需要决定以下几点:

  • 主键:如果我使用完整的 GPS-POINT-ID,我将无法跟踪更改,因为它会随着时间的推移频繁更改。我无法跟踪重点。而且我无法将其链接到它的历史记录。

  • 如果我使用 GPS-Point-ID (XYZ-00XX) 的固定部分作为计算列,我不能将其用作主键,因为同一个点有许多历史记录具有相同的(XYZ-00XX)部分,这将违反主键重复约束。

  • 如果我为每条新记录创建一个标识列,我如何跟踪每个点名称更改并获取每个点 (XYZ-00XX) 的最新测试数据和历史数据。

视图中的示例行附加在快照中。

谢谢

【问题讨论】:

  • 为什么不使用由两列组成的主键:GPS-Point-ID 的固定部分和坐标部分?
  • 我试过了,但是对于同一个 GPS-POINT-ID+Coordinate 部分,会有多个测试,这会使两列重复。
  • Update-Date 添加到密钥中。
  • 你的意思是我会有一个复合键 "GPS-POINTID","COORDINATE","UPDATE-DATE" ?
  • 是的。我会按照字段的顺序使用聚集键。

标签: sql-server database-design primary-key


【解决方案1】:

我建议对没有商业价值的主键使用身份。我会将数据存储在两列中,一列是静态数据,另一列是变化数据。然后,如果有必要,您可以拥有一个计算列,将它们作为一个字段组合在一起。您还可以添加日期字段,以便您可以关注历史记录。静态数据列是将记录联系在一起的标识符。

我假设您出于某种原因不想使用审计来跟踪历史记录。这是我通常会采取的方法。

http://weblogs.asp.net/jongalloway/adding-simple-trigger-based-auditing-to-your-sql-server-database

编辑: 如果在给定日期只能发生一次更新,则示例查询有效。如果可以发生多个更新,则可以使用 row_number 函数而不是 group by。

 Select *
        From Table T1
        Join (Select Max(MatchDate) MatchDate, GpsStaticData 
                 From Table Group By GpsStaticData) T2 
             On T1.GpsStaticData = T2.GpsStaticData And T1.UpdateDate = T2.MatchDate

编辑: 使用 Row_Number()

 With cteGetLatest As
 (
 Select UpdateDate MatchDate, GpsStaticData,
        Row_Number() Over (Partition By GpsStaticData, Order By UpdateDate Desc) SortOrder
)

 Select *
    From Table T1
    Join (Select MMatchDate, GpsStaticData 
                From cteGetLatest Where SortOrder = 1) T2 
            On T1.GpsStaticData = T2.GpsStaticData And T1.UpdateDate = T2.MatchDate

您可以在 row_number 函数中的 Order By UpdateDate 之后添加更多字段以确定选择了哪条记录。

【讨论】:

  • 感谢您的回答,根据您的回答,如何查询“RF-0014”的最新测试结果?如果我不确切知道最新的“坐标后缀”?
  • 我将在我的答案中添加一个示例查询。
  • 这很好,这将连接每个静态点数据和最新的测试日期。它是 100% 完好无损的,但是如果我想积极主动地寻求一个可行的解决方案,如果同一个静态字段同时进行两个测试,它会起作用吗?我还假设这不会作为 PK 的任何字段?
  • 不确定你最后一句话的意思。我将更新查询以使用 row_number 在同一天处理多个更新。
【解决方案2】:

--为了避免人工列开销成本,可以使用复合主键:

-- Simulate the Source View
CREATE TABLE ybSourceView (
    [GPS-POINT-ID] VARCHAR(20),
    [Status] NVARCHAR(MAX),
    UpdateDate [datetime2],
    Reason NVARCHAR(MAX),
    OpId VARCHAR(15)
);

-- Source View sample data
INSERT INTO ybSourceView ([GPS-POINT-ID], [Status], UpdateDate, Reason, OpId)
VALUES ('RF-0014-9876', 'Reachable'   , '2015-01-27 13:36', 'New Updated Coordinate'                 , 'AFERNANDO'),
       ('RF-0014-9876', 'Reachable'   , '2014-02-27 09:37', 'New Updated Coordinate'                 , 'AFERNANDO'),
       ('RF-0014-3465', 'Reachable'   , '2015-04-27 09:42', 'New Updated Coordinate'                 , 'HRONAULD' ),
       ('RF-0014-2432', 'Reachable'   , '2013-06-27 12:00', 'New Updated Coordinate'                 , 'AFERNANDO'),
       ('RF-0015-9876', 'OUT_OF_Range', '2014-04-14 12:00', 'Point Abandoned, getting new coordinate', 'AFERNANDO');

-- Historic Data Table
CREATE TABLE ybGPSPointHistory (
    Area VARCHAR(5) NOT NULL DEFAULT '',
    ID VARCHAR(10) NOT NULL DEFAULT '',
    Coordinates VARCHAR(20) NOT NULL DEFAULT '',
    [GPS-POINT-ID] VARCHAR(20),
    [Status] NVARCHAR(MAX),
    UpdateDate [datetime2] NOT NULL DEFAULT SYSUTCDATETIME(),
    Reason NVARCHAR(MAX),
    OpId VARCHAR(15),
    CONSTRAINT ybGPSPointHistoryPK PRIMARY KEY (Area, ID, UpdateDate) --< Compound Primary Key
);
GO

-- Update Historic Data Table from the Source View
INSERT INTO ybGPSPointHistory (Area, ID, Coordinates, [GPS-POINT-ID], [Status], UpdateDate, Reason, OpId)
SELECT LEFT(Src.[GPS-POINT-ID], LEN(Src.[GPS-POINT-ID]) - 10), RIGHT(LEFT(Src.[GPS-POINT-ID], LEN(Src.[GPS-POINT-ID]) - 5), 4), RIGHT(Src.[GPS-POINT-ID], 4), Src.[GPS-POINT-ID], Src.[Status], Src.UpdateDate, Src.Reason, Src.OpId
FROM ybSourceView Src
LEFT JOIN ybGPSPointHistory Tgt ON Tgt.[GPS-POINT-ID] = Src.[GPS-POINT-ID] AND Tgt.UpdateDate = Src.UpdateDate
WHERE Tgt.[GPS-POINT-ID] Is NULL;

--测试(查看实际执行计划查看PK使用):

-- Full history
SELECT * FROM ybGPSPointHistory;

-- Up-to-date only
SELECT *
FROM (
    SELECT *, RANK () OVER (PARTITION BY Area, ID ORDER BY UpdateDate DESC) As HistoricOrder
    FROM ybGPSPointHistory
) a
WHERE HistoricOrder = 1;

-- Latest record for a particular ID
SELECT TOP 1 *
FROM ybGPSPointHistory a
WHERE [GPS-POINT-ID] = 'RF-0014-9876'
ORDER BY UpdateDate DESC;

-- Latest record for a particular ID in details (more efficient)
SELECT TOP 1 *
FROM ybGPSPointHistory a
WHERE Area = 'RF' AND ID = '0014' AND Coordinates = '9876'
ORDER BY UpdateDate DESC;

-- Latest record for a particular point
SELECT TOP 1 *
FROM ybGPSPointHistory a
WHERE Area = 'RF' AND ID = '0014'
ORDER BY UpdateDate DESC;

--清理:

DROP TABLE ybGPSPointHistory;
DROP TABLE ybSourceView;

【讨论】:

    猜你喜欢
    • 2019-04-29
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-13
    • 2012-04-09
    • 1970-01-01
    相关资源
    最近更新 更多