2010-10-13 22 views
0

J'ai un problème de T-SQL que je trouve difficile à résoudre.Auto-jointure à la plus faible occurrence du groupe

J'ai une table avec des groupes d'enregistrements, regroupés par key1 et key2. Je commande chaque groupe chronologiquement par date. Pour chaque enregistrement, je veux voir s'il existait un enregistrement avant (dans le groupe et avec une date inférieure) pour lequel le champ "datafield" forme une combinaison autorisée avec le "datafield" de l'enregistrement en cours. Pour les combinaisons autorisées, j'ai une table appelée AllowedCombinationsTable.

I écrit le code suivant pour y parvenir:

WITH Source AS (
    SELECT key1, key2, datafield, date1, 
     ROW_NUMBER() OVER(PARTITION BY key1, key2 ORDER BY date1 ASC) AS dateorder 
     FROM table 
) 
SELECT L.key1, L.key2, L.datafield, DC.datafield2 
FROM Source AS L 
LEFT JOIN AllowedDataCombinationsTable DC 
    ON D.datafield1 = L.datafield 
LEFT JOIN Source AS R 
    ON R.Key1 = L.Key1 
    AND R.Key2 = L.Key2 
    AND R.dateorder < L.dateorder 
    AND DC.datafield2 = L.datafield 
    -- AND "pick the one record with lowest dateorder" 

Maintenant, pour chacun de ces enregistrements de combinaisons possibles, je veux choisir le premier (voir espace réservé dans le code). Comment puis-je le faire le plus efficacement?


EDIT: OK, disons que pour la source, ne montrant que le groupe (1, 1):

**Key1 Key2 Datafield Date DateOrder** 
1 1 "Horse" 1-Jan-2010 1 
1 1 "Horse" 2-Jan-2010 2 
1 1 "Sheep" 3-Jan-2010 3 
1 1 "Dog" 4-Jan-2010 4 
1 1 "Cat" 5-Jan-2010 5 

AllowedCombinationsTable:

**Datafield1 Datafield** 
Cat Sheep (and Sheep Cat) 
Cat Horse (and Horse Cat) 
Dog Horse (and Horse Dog) 

Après mon rejoindre J'ai maintenant:

**Key1 Key2 Datafield Date DateOrder JoinedCombination JoinedCombinationDateOrder** 
1 1 "Horse" 1-Jan-2010 1 NULL NULL 
1 1 "Horse" 2-Jan-2010 2 NULL NULL 
1 1 "Sheep" 3-Jan-2010 3 NULL NULL 
1 1 "Dog" 4-Jan-2010 4 "Horse" 1 
1 1 "Dog" 4-Jan-2010 4 "Horse" 2 
1 1 "Cat" 5-Jan-2010 5 "Horse" 1 
1 1 "Cat" 5-Jan-2010 5 "Horse" 2 
1 1 "Cat" 5-Jan-2010 5 "Sheep" 3 

Je veux afficher seulement le premier "Cheval" pour l'enregistrement 4 "Chien", et aussi seulement le premier "Cheval" pour l'enregistrement 5 "Chat".

L'obtenir? ;)

+1

Si vous pouviez fournir une liste table/colonne et expliquer les relations un peu plus clairement, je serais prêt à écrire une réponse. – Fosco

+0

OK, j'ai ajouté quelques exemples de données. – thomaspaulb

Répondre

0

Je pense que cela peut le faire - il n'y a pas de données configurées pour tester la requête. Vérifiez les commentaires pour la justification.

WITH Source AS ( 
    SELECT key1, key2, datafield, date1, 
     ROW_NUMBER() OVER(PARTITION BY key1, key2 ORDER BY date1 ASC) AS dateorder 
     FROM table 
) 
SELECT L.key1, L.key2, L.datafield, DC.datafield2 
FROM Source AS L 
LEFT JOIN AllowedDataCombinationsTable DC 
    ON DC.datafield1 = L.datafield -- DC Alias 
LEFT JOIN Source AS R 
    ON R.Key1 = L.Key1 
    AND R.Key2 = L.Key2 
    AND DC.datafield2 = R.datafield -- Changed alias from L to R 
    AND R.dateorder = 1    -- Pick out lowest one 
    AND R.dateorder < L.dateorder  -- Make sure it's not the same one 
+0

Le L était une faute de frappe en effet. Mais cela ne le fait pas parce que R.dateorder = 1 n'existe pas nécessairement, il peut être filtré par le critère DC.datafield2 = R.datafield. – thomaspaulb

+0

True. Ensuite, la solution consiste à déplacer ces critères de filtrage (ainsi que les jointures de table requises) dans la clause WITH, afin que seules les «lignes candidates» valides soient sélectionnées et classées et filtrent avec RowNumber = 1 dans la clause SELECT. Très difficile de coder correctement sans données à tester, donc je laisse ceci comme commentaire. –

+0

Tout le monde demande des données, mais que suggérez-vous comme moyen pratique de transférer mes données? – thomaspaulb

0

Eh bien, je ne l'utilise WITH ou OVER, c'est donc une approche différente .. Je pourrais être trop simplifier quelque chose, mais sans avoir les données ce que je suis en face de moi avec :

SELECT distinct a.Key1, a.Key2, a.Datafield, 
     ISNULL(b.Datafield,'') as Datafield1, 
     ISNULL(b.Date,a.Date) as `Date`, 
     MIN(a.DateOrder) as DateOrder 
FROM Source a 
LEFT JOIN Source b 
    ON a.Key1 = b.Key1 
    AND a.Key2 = b.Key2 
    AND a.Dateorder <> b.Dateorder 
LEFT JOIN AllowedDataCombinationsTable c 
    ON a.Datafield = c.Datafield 
    AND b.Datafield = c.Datafield1 
GROUP BY a.Key1, a.Key2, a.Datafield, ISNULL(b.Datafield,''), ISNULL(b.Date,a.Date) 
+0

Salut, merci.C'est bien, mais je pense que ça ne marchera pas non plus dans mon cas, puisque j'ai d'autres champs de données que je dois rejoindre. Je vais le tester et revenir vers vous. Pendant ce temps, je l'ai résolu en utilisant une imbrication supplémentaire avec row_number() autour de mon code d'origine afin de sélectionner la rangée la mieux classée par groupe, ce qui fait le travail, mais me semble inefficace. – thomaspaulb