2009-04-21 17 views
9

Należy wybrać tylko pierwszy wiersz z zapytania, które łączy tabele A i B. W tabeli B istnieje wiele rekordów o tej samej nazwie. W żadnej z dwóch tabel nie ma identyfikatorów. Nie mogę zmienić schematu, ponieważ nie jestem właścicielem DB.Wybierz pierwszy wiersz w połączeniu dwóch tabel w jednym oświadczeniu

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 

To daje mi

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

ale muszę odzyskać tylko jeden wiersz na imię

NAME  DATA1 DATA2 
sameName 1  2 
otherName 5  7 

udało mi się to zrobić dodając wynik do tabeli temp z kolumna tożsamości, a następnie wybierz minimalny identyfikator na nazwę.

Problem polega na tym, że wymagam tego w jednym komunikacie.

Odpowiedz

0

Nie jestem pewien, czy rozwiąże to Twój problem, ale możesz spróbować użyć klauzuli GROUP BY i pogrupować według jednej z kolumn nazwy.

DB2 Group by tutorial

7

Korzystanie z GROUP BY może dostać się rozstać sposób tam, ale uważaj. Jeśli zrobisz coś takiego:

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

Dostaniesz wynik szukasz:

NAME  DATA1 DATA2 
    sameName 1  2  
    otherName 5  7 

Ale tylko ze względu na dane, które są z testów. W przypadku zmiany danych, tak że zamiast:

otherName 8  9 

miałeś:

otherName 8  4 

Byłoby powrotu:

NAME  DATA1 DATA2 
    sameName 1  2  
    otherName 5  4 

Zauważ, że otherName nie zwraca dane1 i dane2 od ten sam rekord!

Aktualizacja: Self-join z porównania na jedną z wartości danych może pomóc, takich jak:

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; 

Jednak to będzie działać tylko wtedy, gdy wartości w dane2 są wyjątkowe pod jedną nazwą.

+0

Na ostatnim wyciągu pojawia się drobna literówka. Powinien to być "IS NOT NULL" zamiast "IN NOT NULL". – mbp

+0

+1 jesteś zbawicielem. Twój edited self join działa nawet w SQL CE, gdzie inne rozwiązania tego pytania nie wynikają z ograniczeń SQL CE. –

0

Jeśli możesz dodać do tabeli tymczasowej, a następnie zapytać, możesz to zrobić za jednym razem.

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 

Istnieje wiele innych sposobów, aby to napisać, ale tak właśnie robiłem podobne rzeczy.

0

Spróbuj DeDupe B jak to

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))

Dodaj bardziej lub klauzule, jeśli istnieje więcej kolumn Datax.

Jeśli A zawiera również duplikaty, po prostu użyj DISTINCT jak w OP.

10

to będzie działać:

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 

Jeśli chcesz wybrać najmniejszą wartość danych1 i wewnątrz niej danych2, a następnie skorzystać z tej odmiany:

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 

Oba zapytania dadzą jeden wiersz na Nazwa.

+0

Byłoby lepiej zrobić partycję przez 'B.name', dzięki czemu można użyć indeksu (jeśli taki istnieje). Nie musisz nawet zawracać sobie głowy zgłaszaniem "A.name" (z powodu porównania). –

+1

Świetne rozwiązanie. Dzięki. –

+1

@KobyDouek cieszę się, że pomogło :) Pozdrawiam! –

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) 

To da pożądany wynik, zapewniając wartości B.DATA1 są wyjątkowy w zbiorze dotyczącym tabeli A.

Jeśli nie jesteś wyjątkowy, jedyny inny sposób korzysta wiem CROSS APPLY w MSSQL 2005 i nowszych.

0

Znacznik tego zagadnienia wskazuje, że byłoby to rozwiązanie dla DB2, ale ten jest bardzo podobny do serwera MS-SQL, jeśli tak, spróbuj następujących rozwiązań:

Używanie krzyża, możliwe będzie wyświetlanie co istnieje tylko w obu tabelach

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

Ale jest to możliwe, aby zmienić OUTER wyświetlać co istnieje w bez obowiązku występować w B

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

w structu ponownie w oświadczeniu o stosowaniu, byłoby również możliwe dołączenie instrukcji ORDER, ponieważ nie ma wskazania kolejności wyjść w tabeli B

0

Możesz użyć numeru wiersza, aby uzyskać jeden wiersz dla każdego imienia, spróbować czegoś podobnego poniżej

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 
Powiązane problemy