我有以下问题需要解决,我认为我需要一些帮助,因为我不认为我认为可能的解决方案是正确的:
我有一个带有identity
列的SQL Server表。该表将存储当前数据以及历史数据,如下所述:
我们假设下表:
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 - 删除记录。
在上表PatientId + RecordStatus = 1
中,唯一标识患者的当前记录。
插入新患者时,会为记录分配新的PatientKey
(这由SQL Server引擎自动完成),并且必须计算新的PatientId
并将其分配给记录也是。此外,RecordStatus
设置为1。
当更新现有患者时,更新当前记录以将RecordStatus
设置为等于0.然后,插入具有患者的修改数据的新行。作为插入的一部分,为记录生成新的PatientKey
,但不计算新的PatientId
(即PatientId
不会更改)。此外,RecordStatus
设置为1。
删除患者后,当前记录会更新为RecordStatus
等于2。
问题在于PatientId
的计算。我正在考虑在存储过程中使用MAX(PatientId) + 1
来获取要分配给新记录的新PatientId
,但这将是并发问题。如果多个用户同时创建一个新患者,SP基本上会为他们两个计算相同的PatientId
,然后我会在表格中找到两个不同的患者{{1 }}。
然后,我在考虑将更新放在SQL Server事务中来阻止表,但我认为这太过分了。此外,对于复杂的更新,我认为该表会被阻塞太长时间,其他尝试创建患者会超时。有没有更好的方法来实现这个?
感谢。
答案 0 :(得分:1)
为什么需要PatientKey列和PatientId列?那些不能组合成单个字段吗?由于您的表是患者,因此将您的主键作为PatientId(可能是您的标识列)是有意义的。
假设没有,请考虑使用Computed Column
作为您的PatientId。
这是一本很好的参考指南:
http://msdn.microsoft.com/en-us/library/ms191250(v=sql.105).aspx
祝你好运。答案 1 :(得分:1)
您可以将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
您可以完全解决此问题,方法是让患者表每位患者保留1条记录,并使用patient_log表来维持一段时间内的变化。在更新之前使用并删除触发器以从Patient插入Patient_log。这有很多好处,包括简化查询以获取当前状态,这可能是您执行的最常见查询,表搜索速度更快,因为您没有表上的记录,降低了复杂性并且更好地约束了数据,同时为您提供完全的可审计性,并能够通过一些巧妙的查询创建患者表在特定点上的样子快照。
如果您希望我详细说明这种方法,请给我发电子邮件或发表评论。
答案 2 :(得分:1)
/*
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