2012-07-13 8 views
6

Ho una relazione molti-a-molti tra fatture e transazioni con carta di credito, che sto cercando di mappare somme di insieme. Il modo migliore per pensare al problema è immaginare TransactionInvoiceMap come un grafico bipartito. Per ogni sottografo collegato, trova il totale di tutte le fatture e il totale di tutte le transazioni all'interno di quel sottografo. Nella mia query, voglio restituire i valori calcolati per ciascuno di questi sottografi insieme agli ID delle transazioni a cui sono associati. I totali per le transazioni correlate dovrebbero essere identici.Come creare in modo efficiente sottoinsiemi logici di dati in una tabella di mappatura molti-a-molti?

Più esplicitamente, date le seguenti transazioni/fatture

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 

la mia uscita desiderata è

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

Nota che le fatture 2 e 3 e le operazioni 2 e 3 sono parte di un gruppo logico.

Ecco una soluzione (semplificata, i nomi modificati) che funziona apparentemente, ma è molto lenta. Sto avendo difficoltà a capire come ottimizzare questo, ma penso che comporterebbe l'eliminazione delle subquery in TransactionInvoiceGrouping. Sentiti libero di suggerire qualcosa di radicalmente diverso.

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 
+0

Sono raggruppati perché tutti si "toccano" a vicenda. Se disegnassi delle linee per ogni relazione, ci sarebbe un grafico che copre i quattro oggetti. Pensavo che esistesse un nome di math per questo tipo di raggruppamento, ma non riesco a ricordarlo. –

+0

@StuartBranhan - Quindi sono raggruppati perché hanno una fattura comune ?, è così che funziona ?. Ed è solo per questo motivo che stai facendo $ 150 come somma totale delle transazioni, perché stai aggiungendo l'importo dalla transazione 2 e dalla transazione 3? – Lamak

+0

Sì, o se c'è una transazione comune. La metafora del grafico funziona meglio per spiegarlo. Hai scritto il commento precedente a cui ho risposto? Probabilmente è una buona idea mantenere i commenti in modo che finiscano per avere un senso per i lettori successivi. –

risposta

2

ho implementato la soluzione in un recursive 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 

v'è probabilmente un po 'di spazio per l'ottimizzazione (compreso oggetto denominazione da parte mia!), Ma credo di avere almeno una soluzione corretta che raccoglierà tutte le possibili relazioni Transazione-Fattura da includere nei calcoli.

Non sono riuscito a ottenere le soluzioni esistenti in questa pagina per fornire l'output desiderato dell'OP e sono diventate più brutte in quanto ho aggiunto più dati di test. Non sono sicuro che la soluzione "lenta" postata dall'OP sia corretta come indicato. È molto probabile che io stia interpretando male la domanda.

Ulteriori informazioni:

ho spesso visto che le query ricorsive può essere lento quando si lavora con grandi insiemi di dati. Forse questo può essere l'argomento di un'altra domanda SO. In questo caso, le cose da provare sul lato SQL potrebbero essere limitare l'intervallo (aggiungere clausole where), tabelle di base indice, selezionare prima il CTE in una tabella temporanea, indicizzare quella tabella temporanea, pensare a una condizione di arresto migliore per CTE ... ma prima il profilo, ovviamente.

+0

Strano, ho ottenuto risultati errati anche con la query che ho postato. Devo averlo semplificato in modo errato. C'è una lezione da provare prima sulle variabili della tabella. ;) Ti farà sapere come va con il tuo codice. –

+0

Dopo averlo giocato, la tua soluzione sembra molto più corretta di quella attuale. È ancora molto lento, ma penso che alcune modifiche e le modifiche dell'interfaccia utente sul frontend potrebbero farlo funzionare. Aggiornerò questa risposta con una soluzione finale più tardi. Le CTE ricorsive sono davvero difficili da capire. > _> –

+0

Sono contento che tu abbia iniziato. Ho aggiunto solo alcune possibilità per migliorare questo. Non vedo l'ora di vedere la tua risposta finale e apprezzo l'accetta + taglia. –

0

Se ho capito la domanda giusta, penso che si sta cercando di trovare il minimo di transazioni ID per ogni fattura e ho usato funzione di rango a fare lo stesso.

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 
Problemi correlati