【问题标题】:Why is this SQL code sporadically producing orphaned records?为什么这个 SQL 代码偶尔会产生孤立的记录?
【发布时间】:2017-12-28 00:47:10
【问题描述】:

免责声明:我不是 SQL 专家。我试图在将记录插入父表之前将它们插入子表。 (说完我开始怀疑这是否是一个好主意。)父表记录持有对子表记录的引用,并且所述引用不能为空。这需要我先插入子表,然后在辅助插入期间链接到父表。

无论如何,由于某种原因,此代码会在 IdentificationingData(子)表中随机生成孤立记录,例如,它们在 FraudScore(父)表中没有条目,即使它们应该这样做。

这就是我感到困惑的原因。为了解决这个问题,我开始将@tempFraudScore 表的内容转储到一个物理审计表中,这样我就可以准确地看到数据转换过程中发生了什么。当我将以下插入 FraudScore 的代码从 @tempFraudScore 切换到从审计表中插入时,所有子记录都成功创建了父记录。这对我来说毫无意义。

insert into IdentifyingData (EntryDateTime, IdentifyingDataTypeId, Value, Source)
select distinct GETDATE(), tfs.IdentifyingDataTypeId, tfs.Value, 'SSIS'
from @tempFraudScore tfs
where not exists (
    select id.IdentifyingDataTypeId, id.Value
    from IdentifyingData id
    where tfs.IdentifyingDataTypeId = id.IdentifyingDataTypeId
        and tfs.Value = id.Value
);

update tfs
set tfs.IdentifyingDataId = id.Id
from @tempFraudScore tfs
    inner join IdentifyingData id on
        tfs.Value = id.Value and
        tfs.IdentifyingDataTypeId = id.IdentifyingDataTypeId;

insert into FraudScore (EntryDateTime, FraudCriteriaId, AccountId, IdentifyingDataId, Score, Source)
select distinct
    GETDATE() EntryDateTime,
    tfs.FraudCriteriaId,
    tfs.AccountId,
    tfs.IdentifyingDataId,
    tfs.Score,
    'SSIS'
from @tempFraudScore tfs
    inner join FraudCriteria fc on
        tfs.FraudCriteriaId = fc.Id
            and fc.UniqueEntryPeriod = 0
where not exists (
    select fs.AccountId, fs.FraudCriteriaId, fs.IdentifyingDataId
    from FraudScore fs
    where tfs.AccountId = fs.AccountId
        and tfs.FraudCriteriaId = fs.FraudCriteriaId
        and tfs.IdentifyingDataId = fs.IdentifyingDataId
);

@tempFraudScore 预先填充了所有必要的字段,但 IdentificationDataId 除外;必须通过首先插入到 IdentificationData 中来创建,然后使用创建的 ID 更新变量表。下面是变量表的结构:

declare @tempFraudScore table(
    FraudCriteriaId int,
    AccountId bigint,
    IdentifyingDataId bigint,
    IdentifyingDataTypeId smallint,
    Value varchar(100),
    Score int
);

谁能告诉我是什么导致了这些孤立的识别数据记录?我是否应该重新考虑这两个表之间的关系是如何构建的?我正在尝试做一些事情,以便一旦将某个识别数据记录放入系统中,它就不会被重复;它只会被新创建的 FraudScore 记录引用。

编辑 附件是来自审计表的屏幕截图,显示了单个值的数据转换进度(这些记录的值列是相同的值;为了隐私起见,我将其模糊化)。请注意,尽管出现“Post-FraudScore Insert”消息,但相关记录从未真正插入到 FraudScore 表中。

Edit2 (2/6/2018):我已将以下代码添加到存储过程中以尝试解决此问题。我有一个值 (99999) 出现在 _Audit 表的 Value 列中,但没有出现在第二个表的 Value 列中,尽管代码只是将所有数据从同一源转储到这两个表中!我不确定这是否重要,但这个存储过程是从一个 SSIS 包的执行 SQL 任务中启动的,其 IsolationLevel 为“Serializable”。也就是说,我没有在代码中的任何地方明确使用事务,并且该执行 SQL 任务的 TransactionOption 设置为“支持”。我不知道这是否与这个问题有关。

insert into FraudScoreIdentifyingData_Audit
select 'Post-IdentifyingData Update', GETDATE(), FraudCriteriaId, AccountId, IdentifyingDataId, IdentifyingDataTypeId, Value, Score
from @tempFraudScore;

insert into FraudScoreIdentifyingData
select GETDATE(), FraudCriteriaId, AccountId, IdentifyingDataId, IdentifyingDataTypeId, Value, Score, 1
from @tempFraudScore;

这是两个表的架构:

【问题讨论】:

  • 封闭交易在哪里?
  • @MitchWheat -- 请原谅我的无知,但我不确定你的意思。我有“开始交易”声明吗?
  • @MitchWheat -- 在查找此内容时,您的意思是我的 BEGIN 和 END 语句在哪里?这段代码来自一个存储过程,只有一个 BEGIN 和 END 语句。两者分别位于存储过程的开头和结尾,而我的所有代码(包括您在上面看到的代码)都位于中间。由于查询的复杂性,我应该有更多吗?
  • 提示:在多个语句中使用GetDate() 时,例如在存储过程中,可以通过获取单个值并在整个过程中使用它来避免有趣的意外,即declare @Now as DateTime = GetDate(); 并根据需要使用@Now
  • 我猜最后一个插入(插入 FraudScore)是问题所在?这里有很多谓词和连接将停止插入记录。您需要仔细阅读这些内容。 UniqueEntryPeriod 或任何其他连接列可以为 NULL 吗?你说这些是父子表。关系是一对多还是一对一?每个表的主键是什么?

标签: sql-server database tsql duplicates orphan


【解决方案1】:

不能说是什么导致了问题。

Parent Table=FraudScore

Child Table=IdentifyingData

它们是如何相关的?首先你在FraudScore中插入记录,然后如果你有多个插入,则使用输出子句,在IdentifyingData中插入记录

但这是使用OUTPUT clause 的理想情况,即使问题因此没有解决。

    --data type similar to IdentifyingData
declare @tbl table(Id int,Value int,IdentifyingDataTypeId int)
declare @CurrentDateTime datetime=GETDATE()

begin try
begin transaction

insert into IdentifyingData (EntryDateTime, IdentifyingDataTypeId
, Value, Source)
OUTPUT INSERTED.Id, INSERTED.Value, INSERTED.IdentifyingDataTypeId  
        INTO @tbl  
select distinct @CurrentDateTime, tfs.IdentifyingDataTypeId
, tfs.Value, 'SSIS'
from @tempFraudScore tfs
where not exists (
    select id.IdentifyingDataTypeId, id.Value
    from IdentifyingData id
    where tfs.IdentifyingDataTypeId = id.IdentifyingDataTypeId
        and tfs.Value = id.Value
);


update tfs
set tfs.IdentifyingDataId = id.Id
from @tempFraudScore tfs
    inner join @tbl id on
        tfs.Value = id.Value and
        tfs.IdentifyingDataTypeId = id.IdentifyingDataTypeId;

insert into FraudScore (EntryDateTime, FraudCriteriaId, AccountId, 
IdentifyingDataId, Score, Source)
select distinct
    @CurrentDateTime EntryDateTime,
    tfs.FraudCriteriaId,
    tfs.AccountId,
    tfs.IdentifyingDataId,
    tfs.Score,
    'SSIS'
from @tempFraudScore tfs
    inner join FraudCriteria fc on
        tfs.FraudCriteriaId = fc.Id
            and fc.UniqueEntryPeriod = 0
where not exists (
    select fs.AccountId, fs.FraudCriteriaId, fs.IdentifyingDataId
    from FraudScore fs
    where tfs.AccountId = fs.AccountId
        and tfs.FraudCriteriaId = fs.FraudCriteriaId
        and tfs.IdentifyingDataId = fs.IdentifyingDataId
);
COMMIT
end TRY
begin CATCH
if(@@trancount>0)
ROLLBACK
end CATCH

【讨论】:

    【解决方案2】:

    原来在我的一个大型存储过程中隐藏了一个删除语句,该语句写入不正确导致了问题。

    在寻找这个问题的原因时,我还有一个 DBA 和我坐在一起,他确定了我的 SSIS 流程中正在重组索引的一部分;但它正在这样做,因为包继续运行并填充所有必要的基础表(包括带有孤立记录的表)。据他说,重组或重建表上的索引,同时尝试向这些表中添加或删除记录也可能导致此问题。尽管在我的具体情况下,它是一个错误编写的单个删除语句。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-19
      • 2016-07-23
      • 1970-01-01
      • 2016-04-04
      • 2020-07-17
      • 2020-09-09
      • 2016-02-08
      • 1970-01-01
      相关资源
      最近更新 更多