2013-05-22 50 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:很高興能幫到你。 –