6

請求書とクレジットカード取引の間に多対多の関係があり、それらの合計を一緒にマッピングしようとしています。問題を考える最良の方法は、TransactionInvoiceMap を 2 部グラフとして想像することです。接続された各サブグラフについて、そのサブグラフ内のすべての請求書の合計とすべてのトランザクションの合計を見つけます。私のクエリでは、これらのサブグラフごとに計算された値と、関連付けられているトランザクション ID を返したいと考えています。関連するトランザクションの合計は同じである必要があります。

より明確に、次のトランザクション/請求書を考えると

Table: TransactionInvoiceMap
TransactionID  InvoiceID
1              1
2              2
3              2
3              3

Table: Transactions
TransactionID  Amount
1              $100
2              $75
3              $75

Table: Invoices
InvoiceID  Amount
1          $100
2          $100
3          $50

私の望む出力は

TransactionID  TotalAsscTransactions TotalAsscInvoiced
1              $100                  $100
2              $150                  $150
3              $150                  $150

請求書 2 と 3、およびトランザクション 2 と 3 は論理グループの一部であることに注意してください。

これは明らかに機能するが非常に遅い解決策(簡略化され、名前が変更された)です。これを最適化する方法を理解するのに苦労していますが、TransactionInvoiceGrouping へのサブクエリを削除する必要があると思います。根本的に異なる何かを自由に提案してください。

with TransactionInvoiceGrouping as (
    select 
        -- Need an identifier for each logical group of transactions/invoices, use
        -- one of the transaction ids for this.
        m.TransactionID,
        m.InvoiceID,
        min(m.TransactionID) over (partition by m.InvoiceID) as GroupingID
    from TransactionInvoiceMap m
)
select distinct
    g.TransactionID,
    istat.InvoiceSum as TotalAsscInvoiced,
    tstat.TransactionSum as TotalAsscTransactions
from TransactionInvoiceGrouping g
    cross apply (
        select sum(ii.Amount) as InvoiceSum
        from (select distinct InvoiceID, GroupingID from TransactionInvoiceGrouping) ig
            inner join Invoices ii on ig.InvoiceID = ii.InvoiceID
        where ig.GroupingID = g.GroupingID
    ) as istat
    cross apply (
        select sum(it.Amount) as TransactionSum
        from (select distinct TransactionID, GroupingID from TransactionInvoiceGrouping) ig
            left join Transactions it on ig.TransactionID = it.TransactionID
        where ig.GroupingID = g.GroupingID
        having sum(it.Amount) > 0
    ) as tstat
4

2 に答える 2

2

私は再帰的な CTEでソリューションを実装しました:

;with TranGroup as (
    select TransactionID
        , InvoiceID as NextInvoice
        , TransactionID as RelatedTransaction
        , cast(TransactionID as varchar(8000)) as TransactionChain
    from TransactionInvoiceMap
    union all
    select g.TransactionID
        , m1.InvoiceID
        , m.TransactionID
        , g.TransactionChain + ',' + cast(m.TransactionID as varchar(11))
    from TranGroup g
        join TransactionInvoiceMap m on g.NextInvoice = m.InvoiceID
        join TransactionInvoiceMap m1 on m.TransactionID = m1.TransactionID
    where ',' + g.TransactionChain + ',' not like '%,' + cast(m.TransactionID as varchar(11)) + ',%'
)
, RelatedTrans as (
    select distinct TransactionID, RelatedTransaction
    from TranGroup
)
, RelatedInv as (
    select distinct TransactionID, NextInvoice as RelatedInvoice
    from TranGroup
)
select TransactionID
    , (
        select sum(Amount)
        from Transactions
        where TransactionID in (
            select RelatedTransaction
            from RelatedTrans
            where TransactionID = t.TransactionID
        )
    ) as TotalAsscTransactions
    , (
        select sum(Amount)
        from Invoices
        where InvoiceID in (
            select RelatedInvoice
            from RelatedInv
            where TransactionID = t.TransactionID
        )
    ) as TotalAsscInvoiced
from Transactions t

最適化の余地はおそらくいくらかありますが (オブジェクトの命名も含めて!)、計算に含めるすべての可能なトランザクションと請求書の関係を収集する正しい解決策が少なくともあると思います。

このページの既存のソリューションを取得して、OP の目的の出力を得ることができませんでした。テスト データを追加すると、ソリューションが醜くなりました。OPが投稿した「遅い」ソリューションが正しいかどうかはわかりません。私が質問を誤解している可能性が非常に高いです。

追加情報:

大規模なデータセットを扱う場合、再帰クエリが遅くなることがよくあります。おそらく、それは別のSOの質問の対象になる可能性があります。その場合、SQL 側で試すことは、範囲を制限する (where句を追加する)、ベース テーブルにインデックスを付ける、CTE を最初に一時テーブルに選択する、その一時テーブルにインデックスを付ける、CTE のより適切な停止条件を考えることです。 ...しかし、もちろん、最初にプロファイルします。

于 2012-07-16T21:26:07.890 に答える
0

私が質問を正しく理解していれば、各請求書のトランザクション ID の最小値を見つけようとしていると思います。ランキング機能を使用して同じことをしました。

WITH TransactionInvoiceGrouping AS (
SELECT 
    -- Need an identifier for each logical group of transactions/invoices, use
    -- one of the transaction ids for this.
    m.TransactionID,
    m.InvoiceID,       
    ROW_NUMBER() OVER (PARTITION BY m.InvoiceID ORDER BY m.TransactionID ) AS recno
FROM TransactionInvoiceMap m
)
 SELECT 
g.TransactionID,
istat.InvoiceSum AS TotalAsscInvoiced,
tstat.TransactionSum AS TotalAsscTransactions
 FROM TransactionInvoiceGrouping g
CROSS APPLY(
    SELECT SUM(ii.Amount) AS InvoiceSum
    FROM TransactionInvoiceGrouping ig
        inner JOIN Invoices ii ON ig.InvoiceID = ii.InvoiceID
    WHERE ig.TransactionID = g.TransactionID
    AND ig.recno = 1
) AS istat
CROSS APPLY(
    SELECT sum(it.Amount) AS TransactionSum
    FROM TransactionInvoiceGrouping ig
        LEFT JOIN transactions it ON ig.TransactionID = it.TransactionID
    WHERE ig.TransactionID = g.TransactionID
    AND ig.recno = 1
    HAVING SUM(it.Amount) > 0
) AS tstat

WHERE g.recno = 1
于 2012-07-16T17:30:08.350 に答える