2012-04-10 21 views
5

Per l'impostazione della gestione consolidata dell'account, desidero rilevare gli account che hanno "esattamente lo stesso" insieme di proprietari.Come posso trovare gruppi di record che corrispondono ad altri gruppi di record (divisione relazionale?)

Penso che potrebbe funzionare per ruotare i proprietari con sql dinamico, quindi utilizzare le funzioni di classifica , ma non voglio perseguire tale approccio; Non ho un limite superiore su quanti nomi possono essere associati ad un account dato , quindi voglio evitare l'SQL dinamico.

I miei dati (anche questo è a http://www.sqlfiddle.com/#!3/1d36e)

CREATE TABLE allacctRels 
(account INT NOT NULL, 
module CHAR(3) NOT NULL, 
custCode CHAR(20) NOT NULL) 


INSERT INTO allacctrels 
(account, module, custCode) 
VALUES 
(1, 'DDA', 'Wilkie, Walker'), 
(1, 'DDA', 'Houzemeal, Juvy'), 
(2, 'CDS', 'Chase, Billy'), 
(2, 'CDS', 'Norman, Storm'), 
(3, 'CDS', 'Chase, Billy'), 
(3, 'CDS', 'Norman, Storm'), 
(7, 'CDS', 'Perkins, Tony'), 
(15, 'SVG', 'Wilkie, Walker'), --typo in name before mwigdahl's response 
(16, 'SVG', 'Wilkie, Walker'), -- corrected typo here too 
(606, 'DDA', 'Norman, Storm'), 
(606, 'DDA', 'Chase, Billy'),-- corrected 2nd typo found 
(4, 'LNS', 'Wilkie, Walker'), 
(4, 'LNS', 'Houzemeal, Juvy'), 
(44, 'DDA', 'Perkins, Tony'), 
(222, 'DDA', 'Wilkie, Walker'), 
(222, 'DDA', 'Houzemeal, Juvy'), 
(17, 'SVG', 'Wilkie, Walker'), -- added these three rows in edit, SVG 17 doesn't match any dda 
(17, 'SVG', 'Welch, Raquel'), 
(17, 'SVG', 'Houzemeal, Juvy') 

voglio sapere, per ogni modulo-account, come il più basso DDA conto è che ha le stesse identiche proprietari ad esso associati.

Nei dati di esempio, vorrei questi risultati, la terza colonna è l'account DDA più basso con gli stessi proprietari. I risultati dovrebbero avere lo stesso numero di righe come Therea ri modulo/conto combinazioni - una riga per ciascuna riga "SELECT DISTINCT modulo, account da allAcctRels")

1, DDA, 1 
2, CDS, 606 
3, CDS, 606 
15, SVG, NULL 
16, SVG, NULL 
606, DDA, 606 
4, LNS, 1 
7, CDS, 44 
44, DDA, 44 
222, DDA, 1 
17, SVG, NULL -- added to original post. 

SVG 15 e 16 non corrispondono alcun DDA account, quindi non importa che corrispondano tra loro, ottengono NULL per l'account in cui consolidarsi. MODIFICA: SVG 17 non corrisponde a nulla, anche se esiste un accen DDA che ha tutti i suoi titolari in SVG 17, la combinazione di titolari in SVG 17 non si verifica per alcun accento DDA. Ogni account DDA corrisponderà a se stesso, a meno che non esista un account DDA con gli stessi proprietari e DDA inferiore (come nel caso di DDA 222).

Posso vedere che un approccio generale è di ruotare ogni account, gruppo la tabella pivot e utilizzare row_number. Dato il numero illimitato di titolari di associati a ciascun account, penso che il pivot richiederebbe l'SQL dinamico che preferirei evitare.

Mi sembra che si tratti di un problema di "divergenza relazionale", con la divisione relazionale probabilmente "alimentata" da un'applicazione CROSS. Ho provato scrivendo una funzione che avrebbe preso una tabella di titolari di account associati con un account specifico e trovare l'account DDA più basso, lungo le linee mostrato di seguito, l'idea è di vedere se tutto il numero di persone in un dato l'account è uguale al numero di persone quando quell'account viene aggiunto a a un determinato account dda, ma non riesco a capire come "inserire" le tabelle dei numeri di conto nella funzione.

-- this is what I tried but I'm not sure it the logic would work 
-- and I can't figure out how to pass the account holders for each 
-- account in. This is a bit changed from the function I wrote, some 
    -- extraneous fields removed and cryptic column names changed. So it 
    -- probably won't run as is. 

    -- to support a parameter type to a tape 
-- CREATE type VisionCustomer as Table 
-- (customer varchar(30)) 

CREATE FUNCTION consolidatable 
(@custList dbo.VisionCustomer READONLY) 
RETURNS char(10) 
AS 
BEGIN 
DECLARE @retval Varchar(10) 
DECLARE @howmany int 
select @howmany=Count(*) FROM @custlist; 

SELECT @retval = min (acct) FROM allAcctRels 
    JOIN @custlist 
     On VendorCustNo = Customer 
      WHERE acctType = 'DDA' 
      GROUP BY acct 
      HAVING (count(*) = @howmany) 
      and 
      COUNT(*) = (select Count(*) FROM allAcctRels X 
    WHERE X.acctType = 'DDA' 
    AND X.account = AllAcctRels.account) ; 
RETURN @retval 
END; 
+0

nota che "Chase, Billie" nella riga 606 DDA non strambare con il gruppo di risultati dici che vuoi indietro ; Penso che tu voglia che questo sia "Chase, Billy", giusto? – mwigdahl

+0

Sì, è corretto, mi dispiace per quello, e grazie, mi riedificherò ora –

risposta

1

Questo in realtà risulta piuttosto semplice, se ho capito bene. Prova questo:

SELECT a.account, a.module, MIN(b.account) 
FROM allacctRels a 
    LEFT JOIN allacctRels b ON a.custCode = b.custCode AND b.module = 'DDA' 
GROUP BY a.account, a.module 

MODIFICA: Quanto sopra non funziona dopo i chiarimenti, ma questo dovrebbe. È davvero un tipo di divisione relazionale. Probabilmente non è il piano di query più efficiente al mondo, ma funziona.

SELECT a.account, a.module, MIN(b.account) 
FROM allacctRels a 
    LEFT JOIN allacctRels b ON b.module = 'DDA' 
    AND 
    -- first test is to confirm that the number of matching names for this combination equals the number of names for the DDA set... 
    (
     SELECT COUNT(*) 
     FROM allacctRels b2 
      INNER JOIN allacctRels a2 ON b2.custCode = a2.custCode 
     WHERE a.account = a2.account AND b.account = b2.account 
    ) = 
    (
     SELECT COUNT(*) 
     FROM allacctRels b2 
     WHERE b.account = b2.account 
    ) 
    AND 
    -- second test is to confirm that the number of names for the DDA set equals the number of names for the base set... 
    (
     SELECT COUNT(*) 
     FROM allacctRels b2 
     WHERE b.account = b2.account 
    ) = 
    (
     SELECT COUNT(*) 
     FROM allacctRels a2 
     WHERE a.account = a2.account 
    ) 
GROUP BY a.account, a.module 
+0

+1; questa è la prima cosa che mi è venuta in mente quando stavo guardando questo. – lyrisey

+0

Ma qui si conservano tutte le righe che hanno una corrispondenza sul lato "b" (DDA), vero? Ad esempio, se aggiungo "Welch Raquel" a ogni account non DDA, i risultati per gli acces non DDA non cambieranno, ma non ci dovrebbero essere corrispondenze, poiché nessun acct non DDA ha Raquel Welch. Ho avuto due errori di battitura in "Walker Wilkie" nei miei dati di esempio che hanno reso questa query gli stessi risultati del mio esempio e non avevo alcun esempio nei miei dati di account con una persona "extra" che non dovrebbe corrispondere. Ho aggiornato sql fiddle e aggiornerò anche gli esempi precedenti. –

+0

Grazie per il chiarimento. Lo sto guardando di nuovo. – mwigdahl

1

Credo che questo è ciò che si sta cercando (http://www.sqlfiddle.com/#!3/f96c5/1):

;WITH AccountsWithOwners AS 
(
    SELECT DISTINCT 
    DA.module 
    , DA.account 
    , STUFF((SELECT 
       ',' + AAR.custCode 
       FROM allacctRels AAR 
       WHERE AAR.module = DA.module 
       AND AAR.account = DA.account 
       ORDER BY AAR.custCode 
       FOR XML PATH('')) 
       , 1, 1, '') AS Result 
    FROM allacctRels DA 
) 
, WithLowestDda AS 
(
    SELECT 
     AWO.module 
     , AWO.account 
     , MatchingAccounts.account AS DdaAccount 
     , ROW_NUMBER() OVER(PARTITION BY AWO.module, AWO.account ORDER BY MatchingAccounts.account) AS Row 
    FROM AccountsWithOwners AWO 
    LEFT JOIN AccountsWithOwners MatchingAccounts 
     ON MatchingAccounts.module = 'DDA' 
     AND MatchingAccounts.Result = AWO.Result 
) 
SELECT 
    account 
    , module 
    , DdaAccount 
FROM WithLowestDda 
WHERE Row = 1 
+0

Questo sembra un vincitore, è entrato proprio nel momento in cui devo avviare alcuni backup e tornare a casa, lo guarderò più stasera, ma vedo che i risultati dei miei test hanno risultati corretti. Non ho familiarità con il FOR XML, quindi voglio capire come funziona prima di accettare. –

+0

@LevinMagruder Fondamentalmente sta creando XML che non ha un vero markup e viene utilizzato per creare un elenco separato da virgole. Fatemi sapere se avete domande. –

Problemi correlati