2013-05-22 48 views
1

我试图实施借记贷记解决方案系统,但是我很难根据集合表达逻辑。在SQL查询中实施借记贷记解决方案系统

假设我有订单的表:

Id OrderId  Amount  AdjustmentFlag 
1  1   10.00  0 
2  1   10.00  1 
3  1   10.00  2 
4  2   20.00  1 
5  2   20.00  2 
6  2   20.00  2 
7  3   30.00  1 
8  4   40.00  0 
9  4   40.00  0 
10  4   40.00  1 
11  5   50.00  0 
12  5   50.00  1 
13  5   60.00  2 
14  5   60.00  1 
15  5   60.00  2 
16  5   70.00  1 

我需要挑出Id S中的仍然是基于他们是否有匹配的“取消”标志有效。

0 - Original Order 
1 - Cancelled Order 
2 - Adjusted Order 
  1. 1匹配0或偏好20
  2. A 1如果标志不匹配,则忽略该标志。

鉴于上面的例子:

  • 编号2将匹配ID 1留下标识3.
  • ID 4将匹配或者ID 5或ID 6但不是两者。
  • Id 7将被忽略。
  • Id 10将匹配Id 8或Id 9,但不是两者都匹配。
  • ID 12将匹配ID 11.
  • ID 14将匹配Id 13或Id 15,但不是两者都匹配。
  • Id 16将被忽略。

可能的结果是[1,2,4,5,7,8,10,11,12,13,14,16](较低的ID有偏好)或[1,2,4, 6,7,9,10,11,12,14,15,16](更高的Id有偏好)。只要结果是确定性的,它们都可以工作。

创建脚本:

CREATE TABLE [Order] 
(
    Id INT IDENTITY NOT NULL PRIMARY KEY 
    ,OrderId INT NOT NULL 
    ,Amount MONEY NOT NULL 
    ,AdjustmentFlag TINYINT NOT NULL 
); 

INSERT INTO [Order](OrderId, Amount, AdjustmentFlag) 
SELECT 1, 10.00, 0 
UNION ALL 
SELECT 1, 10.00, 1 
UNION ALL 
SELECT 1, 10.00, 2 
UNION ALL 
SELECT 2, 20.00, 1 
UNION ALL 
SELECT 2, 20.00, 2 
UNION ALL 
SELECT 2, 20.00, 2 
UNION ALL 
SELECT 3, 30.00, 1 
UNION ALL 
SELECT 4, 40.00, 0 
UNION ALL 
SELECT 4, 40.00, 0 
UNION ALL 
SELECT 4, 40.00, 1 
UNION ALL 
SELECT 5, 50.00, 0 
UNION ALL 
SELECT 5, 50.00, 1 
UNION ALL 
SELECT 5, 60.00, 2 
UNION ALL 
SELECT 5, 60.00, 1 
UNION ALL 
SELECT 5, 60.00, 2 
UNION ALL 
SELECT 5, 70.00, 1 

这是我目前的部分解决方案:

WITH Orders AS 
(
    SELECT 
     Id, 
     OrderId, 
     Amount, 
     AdjustmentFlag, 
     EffectiveOrder = ROW_NUMBER() OVER (PARTITION BY OrderId, Amount ORDER BY AdjustmentFlag DESC), 
     UnmatchedOrder = CASE WHEN EXISTS(SELECT 1 FROM [Order] uo WHERE uo.OrderId = o.OrderId GROUP BY uo.OrderId HAVING(COUNT(uo.OrderId) = 1)) THEN 1 ELSE 0 END, 
     OriginalWithoutAdjustment = CASE WHEN EXISTS(SELECT 1 FROM [Order] uo WHERE uo.OrderId = o.OrderId AND uo.Amount = o.Amount GROUP BY uo.OrderId, uo.Amount HAVING (MAX(uo.AdjustmentFlag) = 1)) THEN 1 ELSE 0 END, 
     AdjustmentWithoutOriginal = CASE WHEN EXISTS(SELECT 1 FROM [Order] uo WHERE uo.OrderId = o.OrderId AND uo.Amount = o.Amount GROUP BY uo.OrderId, uo.Amount HAVING (MIN(uo.AdjustmentFlag) = 1)) THEN 1 ELSE 0 END 
    FROM [Order] o 
) 
,MatchedOrders AS 
(
    SELECT 
     Id 
    FROM Orders 
    WHERE 
    -- Assume AdjustmentFlag = 2 and take everything else 
    EffectiveOrder <> 1 
    OR 
    (
     -- Assume AdjustmentFlag = 2 and there is no Order with AdjustmentFlag = 0 
     -- Take everything since the MIN AdjustmentFlag = 1 
     AdjustmentWithoutOriginal = 1 
     AND EffectiveOrder > 1 
    ) 
    OR 
    (
     -- Assume AdjustmentFlag = 1 and there are no other Orders, so ignore it 
     AdjustmentFlag = 1 
     AND UnmatchedOrder = 1 
    ) 
    OR 
    (
     -- We don't care about the orders if they don't have any Amount 
     Amount = 0 
     AND EffectiveOrder = 1 
    ) 
    AND NOT 
    (
     -- We have an Original without any other Orders 
     EffectiveOrder = 1 
     AND UnmatchedOrder = 1 
     AND AdjustmentFlag = 0 
    ) 
) 
SELECT 
    o.OrderId, 
    o.AdjustmentFlag, 
    o.Amount, 
    o.EffectiveOrder, 
    o.UnmatchedOrder, 
    Excluded = CASE WHEN mo.Id IS NULL THEN 0 ELSE 1 END 
FROM Orders o 
LEFT OUTER JOIN MatchedOrders mo 
ON o.Id = mo.Id 
ORDER BY OrderId, Amount, AdjustmentFlag 

结果:

Result

+0

对于同一OrderId,取消订单的金额是否总是与原始金额或调整后订单的金额完全一致?您可以为同一OrderId有多个取消的订单记录吗? –

+0

是取消的订单必须完全匹配。是的,可以有多个取消订单,我会发布一个示例。 – Romoku

+0

我曾经工作过的地方叫做和解。 – Jodrell

回答

2

尝试:

with cte as 
(select o.*, 
     case AdjustmentFlag when 1 then -1 else 1 end DrCr, 
     row_number() over (partition by OrderId, Amount, case AdjustmentFlag when 1 then 1 end 
          order by AdjustmentFlag, Id) Rn 
from [Order] o) 
select OrderId, 
     max(case DrCr when 1 then Id end) DrId, 
     sum(case DrCr when 1 then Amount else 0 end) DrAmount, 
     max(case DrCr when 1 then AdjustmentFlag end) DrAdjustmentFlag, 
     max(case DrCr when -1 then Id end) CrId, 
     sum(case DrCr when -1 then Amount else 0 end) CrAmount, 
     max(case DrCr when -1 then AdjustmentFlag end) CrAdjustmentFlag, 
     sum(DrCr * Amount) BalanceAmount 
from cte 
group by OrderId, Amount, Rn 
having sum(DrCr * Amount) >= 0 /* excludes unmatched cancelled orders */ 

- 如果您只想查看不匹配的原始/修正订单,请将having子句条件更改为> 0

SQLFiddle here

+0

嗯,我猜我在想反向(获得所有匹配的Id),因为这会让我获得所有未取消的Id(最终动作),这正是我想要的。谢谢。 – Romoku

+0

@Romoku:很高兴能帮到你。 –