2009-04-21 7 views
9

Je dois sélectionner uniquement la première ligne d'une requête qui joint les tables A et B. La table B existe plusieurs enregistrements de même nom. Il n'y a pas d'identifiants dans les deux tables. Je ne peux pas changer le schéma non plus parce que je ne possède pas la base de données.Sélectionnez la première ligne dans une jointure de deux tables dans une instruction

TABLE A 
NAME 

TABLE B 
NAME 
DATA1 
DATA2 

Select Distinct A.NAME,B.DATA1,B.DATA2 
From A 
Inner Join B on A.NAME = B.NAME 

Cela me donne

NAME  DATA1 DATA2 
sameName 1  2 
sameName 1  3 
otherName 5  7 
otherName 8  9 

mais je dois récupérer une seule ligne par nom

NAME  DATA1 DATA2 
sameName 1  2 
otherName 5  7 

j'ai pu le faire en ajoutant le résultat dans une table temporaire avec un colonne d'identité, puis sélectionnez l'ID minimum par nom.

Le problème ici est que j'ai besoin de le faire en une seule instruction.

Répondre

0

Vous ne savez pas si cela va résoudre votre problème ou non, mais vous pouvez essayer d'utiliser la clause GROUP BY et le groupe par l'une des colonnes de nom.

DB2 Group by tutorial

7

En utilisant GROUP BY peut vous faire partie chemin, mais méfiez-vous. Si vous faites quelque chose comme ceci:

Select A.NAME, min(B.DATA1), min(B.DATA2) 
From A Inner Join B on A.NAME = B.NAME 
Group by A.NAME; 

Vous obtiendrez le résultat que vous recherchez:

NAME  DATA1 DATA2 
    sameName 1  2  
    otherName 5  7 

Mais seulement en raison des données que vous testez avec. Si vous modifiez les données, de sorte qu'au lieu de:

otherName 8  9 

vous aviez:

otherName 8  4 

Il renverrait:

NAME  DATA1 DATA2 
    sameName 1  2  
    otherName 5  4 

Notez que otherName ne retourne pas DATA1 et DATA2 de la même enregistrement!

Mise à jour: Une auto-jointure avec une comparaison sur l'une des valeurs de données peuvent vous aider, telles que:

SELECT a.*, b.* FROM a,b 
    LEFT JOIN b b2 ON b.name = b2.name AND b.data2 < b2.data2 
    WHERE a.name = b.name AND b2.data2 IS NOT NULL; 

Cependant, cela ne fonctionnera que si les valeurs DATA2 sont uniques par NOM.

+0

Il y a une petite faute de frappe sur la dernière déclaration. Il devrait être "IS NOT NULL" au lieu de "IN NOT NULL". – mbp

+0

+1 vous êtes un sauveur. Votre auto-jointure modifiée fonctionne même avec SQL CE où d'autres solutions pour cette question ne sont pas dues aux limitations de SQL CE. –

0

Si vous pouvez ajouter à une table temporaire, puis interroger à partir de cela, vous pouvez le faire en une seule fois.

WITH T AS (temp table select), RN AS (select min row-numbers from T) SELECT T.NAME, T.DATA1, T.DATA2 FROM T INNER JOIN RN on T.row_number = RN.row_number 

Il existe de nombreuses autres façons d'écrire ceci, mais c'est comme cela que j'ai fait des choses similaires.

0

Essayez de Dedupe B comme celui-ci

SELECT A.NAME, bb.DATA1, bb.DATA2 
FROM A 
JOIN B bb 
ON  A.NAME = B.NAME 
WHERE NOT EXISTS (SELECT * 
        FROM B 
        WHERE NAME = bb.NAME 
          AND (DATA1 > bb.DATA1 
           OR DATA1 = bb.DATA1 AND DATA2 > bb.DATA2))

Ajouter plus ou clauses si d'autres existent colonnes Datax.

Si A contient également des doublons, utilisez simplement DISTINCT comme OP.

10

Cela fonctionne:

with temp as (
    select A.NAME, B.DATA1, B.DATA2, 
     row_number() over (partition by A.NAME order by A.NAME) as rownum 
    from TABLEA A inner join TABLEB B 
    on A.NAME = B.NAME 
) 
select NAME, DATA1, DATA2 from temp where rownum = 1 

Si vous souhaitez sélectionner la moindre valeur de data1 et en son sein données2, utilisez cette variation:

with temp as (
    select A.NAME, B.DATA1, B.DATA2, 
     row_number() over (partition by A.NAME order by B.DATA1, B.DATA2) as rownum 
    from TABLEA A inner join TABLEB B 
    on A.NAME = B.NAME 
) 
select NAME, DATA1, DATA2 from temp where rownum = 1 

Les deux requêtes donnera une ligne par prénom.

+0

Il serait préférable de faire la partition par 'B.name', donc vous pouvez utiliser un index (s'il en existe un). Vous n'auriez même pas besoin de signaler 'A.name' (à cause de la comparaison). –

+1

Excellente solution. Merci. –

+1

@KobyDouek heureux que ça a aidé :) cheers! –

0
SELECT A.NAME, bb.DATA1, bb.DATA2 
From A Inner Join B on A.NAME = B.NAME 
WHERE B.DATA1 = (SELECT MIN(DATA1) FROM B WHERE NAME = A.NAME) 

Cela donnera le résultat souhaité, fournissant des valeurs de B.DATA1 sont uniques dans l'ensemble concernant le tableau A.

Si elles ne sont pas uniques, la seule façon que je connaisse utilise CROSS APPLY dans MSSQL 2005 et au-dessus.

0

L'étiquette de cette question indique que ce serait une solution pour DB2, mais cela est très similaire au serveur MS-SQL, si oui essayez ces solutions:

Utilisation CROSS, il sera possible d'afficher ce existe que dans les deux tables

select A.*, B.DATA1, B.DATA2 
from A 
cross apply (select top 1 * from B where B.name = A.name) B 

Mais il est possible de changer OUTER pour afficher ce qui existe dans A sans l'obligation d'exister dans B

select A.*, B.DATA1, B.DATA2 
from A 
OUTER apply (select top 1 * from B where B.name = A.name) B 

dans la structu En ce qui concerne l'instruction apply, il serait également possible d'inclure une instruction ORDER, car il n'y a aucune indication de l'ordre des sorties dans la table B

0

Vous pouvez utiliser le numéro de ligne pour obtenir une ligne pour chaque nom, essayez quelque chose comme ci-dessous

Select name,data1,data2 from 
(Select A.NAME,B.DATA1,B.DATA2,row_number() over(partitioj by a.name order by a.name) rn 
From A 
Inner Join B on A.NAME = B.NAME) where rn=1 
Questions connexes