2009-04-25 12 views
18

Buduję aplikację, która dynamicznie generuje sql w celu wyszukania wierszy określonej tabeli (jest to główna klasa domeny, podobnie jak pracownik).Uzyskiwanie różnych wierszy od lewego zewnętrznego łączenia

Dostępne są trzy tabele Tabela1, Tabela2 i Tabela1 Tabela2Mapa. Tabela 1 ma wiele do wielu relacji z tabelą 2 i jest odwzorowywana za pomocą tabeli Table1Table2Map. Ale ponieważ tabela 1 jest moją główną tabelą, relacja jest praktycznie jak jedna do wielu.

Moja aplikacja generuje sql, który zasadniczo daje zestaw wyników zawierający wiersze ze wszystkich tych tabel. Klauzula wyboru i sprzężenia nie zmieniają się, podczas gdy klauzula where jest generowana na podstawie interakcji użytkownika. W każdym razie nie chcę duplikatów wierszy tabeli 1 w moim zestawie wyników, ponieważ jest to tabela główna do wyświetlania wyników. Teraz zapytanie generowane jest w następujący sposób:

select distinct Table1.Id as Id, Table1.Name, Table2.Description from Table1 
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id) 
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id) 

Dla uproszczenia wyłączyłem klauzulę where. Problem polega na tym, że w tabeli 2 dla tabeli 1 znajduje się wiele wierszy, nawet jeśli powiedziałem, że są odmienne od Table1.Id zestaw wyników ma zduplikowane wiersze tabeli 1, ponieważ musi wybrać wszystkie pasujące wiersze w tabeli 2.

Aby opracować więcej, należy wziąć pod uwagę, że dla wiersza w Tabeli 1 o Id = 1 są dwa wiersze w Tabeli 1 Tabela 2 Mapa (1, 1) i (1, 2) Tablica odwzorowań 1 do dwóch wierszy w Tabeli 2 z ids 1, 2. powyżej wspomniana kwerenda zwraca zduplikowane wiersze dla tego przypadku. Teraz chcę, aby zapytanie zwróciło wiersz Table1 o identyfikatorze 1 tylko jeden raz. Wynika to z tego, że w tabeli 2 znajduje się tylko jeden wiersz, który jest jak aktywna wartość dla odpowiedniego wpisu w tabeli 1 (ta informacja znajduje się w tabeli mapowania). Czy istnieje sposób uniknięcia powtarzania wierszy tabeli 1.

Myślę, że jest jakiś podstawowy problem w sposobie, w jaki próbuję rozwiązać problem, ale nie jestem w stanie dowiedzieć się, co to jest. Z góry dziękuję.

+0

Jak odróżnić, który wiersz jest "aktywny" w tabeli 2? – Sung

Odpowiedz

22

Spróbuj:

left outer join (select distinct YOUR_COLUMNS_HERE ...) SUBQUERY_ALIAS on ... 

Innymi słowy, nie łączą się bezpośrednio na stole, dołącz przeciwko sub-kwerendy, która ogranicza wiersze, które łączą przeciw.

+2

Proszę podać pełny przykład, więc byłoby to bardziej zrozumiałe, ponieważ jestem tutaj zagubiony .. dzięki :) – iSafa

+1

Oto dokładniejszy przykład z dodatkowym filtrem według opisu: 'SELECT t1.Id, t1.Name, tAlias. Opis FROM t1 LEFT JOIN (SELECT id, nazwisko FROM t2 GDZIE opis LIKE 'Witaj%' GROUP BY id, nazwisko ), jak tAlias ​​NA t1.Id = tAlias.Id I t1.Name = tAlias.Name ' –

+0

dziękuję, to działa! :) – kirti

1

Jeśli chcesz wyświetlić wiele wierszy z tabeli 2, będziesz mieć zduplikowane dane z wyświetlonej tabeli1. Jeśli chcesz, możesz użyć funkcji agregującej (IE Max, Min) w table2, to wyeliminowałoby duplikaty wierszy z tabeli1, ale również ukryłoby część danych z tabeli2.

Zobacz także moją odpowiedź na pytanie #70161 dla dodatkowych wyjaśnień

3

Wypracowanie na jednym punkcie: mówiłeś, że istnieje tylko jeden „aktywny” wiersz w tabela2 na wiersz w tabeli 1.. Czy ten wiersz nie jest oznaczony jako aktywny, tak aby można go było umieścić w klauzuli where? A może magia w warunkach dynamicznych dostarczanych przez użytkownika decyduje o tym, co jest aktywne, a co nie.

Jeśli nie musisz wybierać czegokolwiek z Tabeli 2, rozwiązanie jest względnie proste, ponieważ możesz użyć funkcji EXISTS, ale ponieważ umieściłeś TAble2.Description w klauzuli, zakładam, że tak nie jest.

Zasadniczo, co oddziela odpowiednie wiersze w tabeli 2 od nieistotnych? Czy jest to flaga aktywna czy dynamiczna? Pierwszy rząd? Tak naprawdę powinieneś usuwać duplikaty.

DISTINCT clauses tend to be overused.Może się tak nie zdarzyć, ale wydaje się, że możliwe jest, że próbujesz przełamać pożądane wyniki za pomocą DISTINCT, zamiast rozwiązać prawdziwy problem, który jest dość powszechnym problemem.

+0

Masz rację, mimo że istnieje tylko jeden aktywny wiersz w tabeli 2 na wiersz w tabeli 1 Muszę uwzględnić klauzulę where na podstawie wyboru użytkownika. – Nazgul

1

Musisz zawierać klauzulę działalność w swojej join (i nie ma potrzeby na odrębny):

select Table1.Id as Id, Table1.Name, Table2.Description from Table1 
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id) and Table1Table2Map.IsActive = 1 
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id) 
+0

Table1Table2Map.IsActive = 1 klauzula nie zawsze może być zawarta w zapytaniu, jest uwzględniana na podstawie wyboru użytkownika. – Nazgul

6

Można ponownie napisać lewą łączy się zewnętrzna dotyczy, dzięki czemu można korzystać z TOP 1 i zlecenie przez następująco:

select Table1.Id as Id, Table1.Name, Table2.Description 
from Table1 
outer apply (
    select top 1 * 
    from Table1Table2Map 
    where (Table1Table2Map.Table1Id = Table1.Id) and Table1Table2Map.IsActive = 1 
    order by somethingCol 
) t1t2 
outer apply (
    select top 1 * 
    from Table2 
    where (Table2.Id = Table1Table2Map.Table2Id) 
) t2; 

Należy zauważyć, że zewnętrzna stosować bez „góra” lub „zamówienie przez” jest dokładnie równoznaczne z LEFT oUTER JOIN, to po prostu daje trochę więcej kontroli. (zastosowanie krzyża jest równoważne złączeniu wewnętrznemu).

Można też zrobić coś podobnego przy użyciu row_number() funkcja:

select * from (
     select distinct Table1.Id as Id, Table1.Name, Table2.Description, 
     rowNum = row_number() over (partition by table1.id order by something) 
     from Table1 
     left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id) 
     left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id) 
) x 
where rowNum = 1; 

Większość tego nie stosuje się, jeżeli IsActive flag można zawęzić inne stoliki do jednego wiersza, ale mogą przyjść przydatne dla ciebie.

+1

Używanie zewnętrznego zastosowania, jak pokazy Johna, jest tutaj przydatne, jeśli nie potrzebujesz tylko filtrować np. Flaga IsActive, ale także musi dopasować niektóre kolumny od lewej tabeli do prawej w klauzuli where (której nie można zrobić w prostym sprzężeniu zewnętrznym). –

11

Możesz użyć GROUP BY na Table1.Id, a to pozbędzie się dodatkowych wierszy. Nie musisz martwić się o mechanikę po stronie sprzężenia.

Wpadłem na to rozwiązanie w ogromnym zapytaniu i to rozwiązanie nie wpłynęło zbytnio na czas zapytania.

UWAGA: Odpowiadam na to pytanie 3 lata po zadaniu pytania, ale może to pomóc komuś w to wierzyć.

+0

Z serwerem SQL zazwyczaj oznacza to, że będziesz musiał grupować według cholernie blisko każdej wybranej kolumny. – Amalgovinus

Powiązane problemy