【发布时间】:2017-07-26 06:50:21
【问题描述】:
我有一个由“声明”组成的数据模型(为了让 stackoverflow 变得简单)只有一个 OpenAmount 字段。还有另外两个表,“ClaimCoupling”和“ClaimEntryReference”。
ClaimCoupling 表直接引用回 Claim 表,ClaimEntryReference 实际上是对可在多个索赔中登记的已收到金额的预订(请参阅 ClaimEntry_ID)。看这张图;
为简单起见,我删除了所有金额,因为这不是我目前正在努力解决的问题。
我想要的是一个查询,该查询将从@Claim 表开始,并获取所有 OpenAmount 为 0 的索赔。但是我希望能够打印出关于此 OpenAmount 是如何产生的准确报告,这意味着我还需要打印出与此声明相关的任何声明。为了使它更有趣,同样的事情也适用于预订,如果在索赔 X 和索赔 Y 上进行了预订,并且只有 X 有未结金额,我想同时获取 X 和 Y,这样我就可以显示已预订的付款作为一个整体。
我尝试使用递归 CTE 来执行此操作,但这(正确地)在循环引用上失败了。我想我会用一个简单的 where 语句来解决这个问题,我会说只递归地添加还不是 CTE 一部分的记录,但这是不允许的....
WITH coupledClaims AS (
--Get all unique combinations
SELECT cc.SubstractedFromClaim_ID AS Claim_ID,
cc.AddedToClaim_ID AS Linked_Claim_ID FROM dbo.ClaimCoupling cc
UNION
SELECT cc.AddedToClaim_ID AS Claim_ID,
cc.SubstractedFromClaim_ID AS Linked_Claim_ID FROM dbo.ClaimCoupling cc
),
MyClaims as
(
SELECT * FROM Claim WHERE OpenAmount <> 0
UNION ALL
SELECT c.* FROM coupledClaims JOIN MyClaims mc ON coupledClaims.claim_id = mc.ID JOIN claim c ON c.ID = coupledClaims.linked_Claim_ID
WHERE c.ID NOT IN (SELECT ID FROM MyClaims)
)
SELECT * FROM MyClaims
在摆弄了太久之后,我决定用一个实际的循环来做......@@Rowcount 并简单地将它们手动添加到一个表变量中,但是当我正在编写这个解决方案时(我正在确定我可以开始工作了)我想我会先在这里问,因为我不喜欢在 TSQL 中编写循环,因为我总是觉得它丑陋且效率低下。
有关数据模型和一些测试数据,请参见以下 sql Fiddle(我将递归部分注释掉,否则我不允许创建链接);
http://sqlfiddle.com/#!6/129ad5/7/0
我希望这里有人能很好地处理这个问题(可能我在递归 CTE 上做错了)。为了完成,这是在 MS SQL 2016 上完成的。
【问题讨论】:
-
检测和处理数据循环的一种方法是here。当您递归遍历数据时,每一行都会跟踪为到达它而探索的路径。任何复制路径上已有元素的新行都会被忽略。
-
老实说,这是处理循环的非常聪明的方法。我可能会花一些时间将其放入,然后检查这两种解决方案的性能,因为如果我没记错的话,它实际上会循环“一次”,然后再检测到它。
-
好的,所以我按照您建议的方式重建查询,并发现这在性能方面比自己进行递归稍微差一些。这可能是由于将 ID 转换为 varchar 然后连接字符串。公平地说,由于它是许多不同的语句,因此分析起来很痛苦,而且我无法获得整个查询的 IO/CPU 统计信息(而不是每个语句的语句)
标签: sql sql-server tsql recursion common-table-expression