【问题标题】:More than one identity column in a SQL Server tableSQL Server 表中有多个标识列
【发布时间】:2013-10-15 23:58:39
【问题描述】:

我有以下问题要解决,我认为我需要一些帮助,因为我认为我认为的可能解决方案不正确:

我有一个带有 identity 列的 SQL Server 表。该表将存储当前数据以及历史数据,如下所述:

  1. 假设如下表:

    CREATE TABLE Patient 
    (PatientKey int IDENTITY(1,1) NOT NULL,
     PatientId int,
     UpdateDate datetime NOT NULL,
     UpdateUser int NOT NULL,
     RecordStatus tinyint NOT NULL)
    

PatientKey 是主键,也是标识列。 PatientId 是唯一标识一个患者(当前记录以及历史记录)。 UpdateDate 是插入或更新患者的日期。 UpdateUser 是插入或更新患者的用户的密钥。 RecordStatus 表示记录的状态:0-历史,1-当前,2-已删除。

  1. 上表中PatientId + RecordStatus = 1是唯一标识一个患者的当前记录。

  2. 当插入一个新患者时,一个新的PatientKey 被分配给记录(这由 SQL Server 引擎自动完成)并且一个新的PatientId 也必须被计算并分配给记录。此外,RecordStatus 设置为 1。

  3. 更新现有患者时,更新当前记录以将RecordStatus 设置为0。然后,插入新行,其中包含患者的修改数据。作为插入的一部分,会为记录生成一个新的PatientKey,但不会计算新的PatientId(即PatientId 不会改变)。此外,RecordStatus 设置为 1。

  4. 当一个病人被删除时,当前记录被更新为设置RecordStatus等于2。

问题在于PatientId 的计算。我正在考虑在存储过程中使用MAX(PatientId) + 1 来获取新的PatientId 以分配给新记录,但这将是并发问题。如果多个用户同时创建一个新患者,SP 基本上会为他们两个计算相同的PatientId,然后我会在表中使用相同的PatientId 结束两个不同的患者。

然后,我正在考虑将更新放入 SQL Server 事务中以阻止表,但我认为这太过分了。此外,对于复杂的更新,我认为该表会被阻塞太久,并且其他创建患者的尝试会超时。有没有更好的方法来实现这一点?

谢谢。

【问题讨论】:

  • lelewin,能详细点吗?
  • 在 SQL Server 2012 中,您的PatientId 可以有一个SEQUENCE(它就像一个标识列,但不绑定到一个表)

标签: sql sql-server stored-procedures identity-column


【解决方案1】:

为什么需要 PatientKey 列和 PatientId 列——不能将它们组合为一个字段吗?由于您的表是 Patient,因此将主键设置为 PatientId 是有意义的,这可能是您的身份列。

假设不是,请考虑为您的 PatientId 使用 Computed Column

这是一个很好的参考指南:

http://msdn.microsoft.com/en-us/library/ms191250(v=sql.105).aspx

祝你好运。

【讨论】:

  • 患者键作为主键,而 PatientId 每个患者都是唯一的,但可以在表中多次出现。这是一个审核设置,因此您可以查看更新的日期并了解患者在某个时间点的情况。
  • 我建议将其转换为 2 个表,一个用于具有 PatientId 作为身份的 Patient,另一个用于链接到该 PatientId 的 PatientAuditing。祝你好运。
【解决方案2】:

您可以只使用 PatientKey 作为 PatientId(创建新患者时),然后使用该 ID。

我会看到插入 SP 的工作方式如下:

-- Insert A new record with some values
INSERT INTO Patient (UpdateDate, UpdateUser, RecordStatus) 
SELECT @UpdateDate, @UpdateUser, @RecordStatus
     --if patient id is `not null` then just use a place holder value like 1 or 0

--update the new record with the UNIQUE patientKey
UPDATE Patient 
SET PatientId = PatientKey
WHERE PatientKey = SCOPE_IDENTITY() 

SELECT SCOPE_IDENTITY() --to return the new id 

您可以通过使用 Patient 表来完全解决此问题,该表为每位患者保存 1 条记录,并使用 Patient_log 表来维护随时间发生的更改。使用更新前和删除触发器从 Patient 插入 Patient_log。这有很多好处,包括简化查询以获得当前状态,这可能是您执行的最常见的查询,表搜索的性能更快,因为表上没有那么多记录,降低复杂性和更好的数据约束,同时为您提供全面的可审计性,并能够通过一些巧妙的查询创建患者表在特定时间点的快照。

如果您希望我详细说明这种方法,只需给我发电子邮件或发表评论。

【讨论】:

    【解决方案3】:
    /*
    
       this trigger exhibits the following behavior:
       for one or more inserts into the Patient table
       where the inserted record did not explicitly provide a value for PatientId
       the patient id is cacluated using the ROW_NUMBER function and
       an arbitrarily chosen seed value such that each new inserted row
       gets the next available patient id
    
       works for single or bulk inserts,
       only assigned patient ids to records who were inserted without one.
    
       NOTE 1: your inserts will probably need to be made within a transaction to avoid problems.
               i'll leave that for commentors to pick on ;)
    
       NOTE 2: you can then write an udpate trigger to ensure the immutability of PatientId
    
    */
    CREATE TRIGGER [Rewards].[Trigger_Patient_Id]
       ON Patient
    AFTER INSERT
    AS
    BEGIN
       SET NoCount ON
    
       /* create arbitrary seed value, just like an identity field */
       DECLARE @PatientIdSeed int = 512; 
    
       /* get the highest id currently in the database greater than the seed value */
       DECLARE @MaxPatientId int = (SELECT ISNULL(MAX(PatientId), @PatientIdSeed) FROM [Patient] WHERE PatientId > @PatientIdSeed) ;
    
       /* update the patient table... */
       UPDATE 
          [Patient]
       SET 
          /* ... getting new patient id ... */
          PatientId = NewPatientId
       FROM
          /* ... by joining the patient table ... */
          [Patient] AS Patient
          JOIN
             /* ... to this sub-query, that uses the ROW_NUMBER() function
                    over the inserted table + MaxPatientId to get new PatientIds */
             (SELECT 
                inserted.Id,
                @MaxPatientId + ROW_NUMBER() OVER(ORDER BY inserted.Key) AS NewPatientId
              FROM 
                inserted
              /* remove this where clause if you wish to overwrite PatientId regardless of insert */
              WHERE
                inserted.PatientId IS NULL
             ) AS newinserts
             ON Patient.Key = newinserts.Key
    END
    

    【讨论】:

      猜你喜欢
      • 2010-09-25
      • 1970-01-01
      • 1970-01-01
      • 2017-07-15
      • 1970-01-01
      • 1970-01-01
      • 2013-12-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多