2014-09-18 9 views
5

Czy istnieje sposób na użycie przecięcia bez wybierania odrębnych wartości? Coś jak INTERSECT ALL.Przecięcie w SQL Server

Rozważmy na przykład tabela A i B

A --> 1, 1, 1, 2, 3, 4 

B --> 1, 1, 2 

skutkowałoby

Result --> 1, 1, 2 

EDIT

Myślę, że to wyjaśnia link dobrze, czego chcę. Ten other link jest również interesujący, aby zrozumieć pytanie. Lub this other link lepiej tłumaczy wydarzenie.

EDIT 2

Załóżmy tabel:

Tabela A

╔════════╦════╦═══╦════╦════╗ 
║ A ║ B ║ C ║ D ║ E ║ 
╠════════╬════╬═══╬════╬════╣ 
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║ 
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║ 
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║ 
║ House ║ 10 ║ 1 ║ NO ║ -5 ║ 
║ Monkey ║ 15 ║ 1 ║ OK ║ -1 ║ 
║ Dog ║ 3 ║ 1 ║ OK ║ -1 ║ 
╚════════╩════╩═══╩════╩════╝ 

Tabela B

╔═════╦════╦═══╦════╦════╗ 
║ A ║ B ║ C ║ D ║ E ║ 
╠═════╬════╬═══╬════╬════╣ 
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║ 
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║ 
║ Car ║ 15 ║ 1 ║ OK ║ -1 ║ 
║ Dog ║ 3 ║ 1 ║ OK ║ -1 ║ 
╚═════╩════╩═══╩════╩════╝ 

Odpowiedź na przecinają (select * from A INTERSECT select * from B) to:

╔═════╦════╦═══╦════╦════╗ 
║ A ║ B ║ C ║ D ║ E ║ 
╠═════╬════╬═══╬════╬════╣ 
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║ 
║ Dog ║ 3 ║ 1 ║ OK ║ -1 ║ 
╚═════╩════╩═══╩════╩════╝ 

Ponieważ zajmuje tylko różne wartości. Co chcę jest podejmowanie wspólnych wierszy, jak:

╔═════╦════╦═══╦════╦════╗ 
║ A ║ B ║ C ║ D ║ E ║ 
╠═════╬════╬═══╬════╬════╣ 
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║ 
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║ 
║ Dog ║ 3 ║ 1 ║ OK ║ -1 ║ 
╚═════╩════╩═══╩════╩════╝ 

przestrzegać nie muszę wiedzieć, co mam połączyć (połączenie jest pozycyjny, podobnie jak INTERSECT). Identyfikator byłby czymś skonstruowanym przy użyciu wszystkich kolumn (połączenie między tabelami to wszystkie kolumny, w zależności od ich położenia).

+0

HUH? Dlaczego wynik ma dwie wartości dla 1? To nie jest to, co INTERSECT w ogóle oznacza. Jest przeznaczony do zwracania różnych wartości, które występują w obu. Aby to zadziałało, potrzebujesz czegoś innego niż przecięcie. –

+0

To jest przykład. Połączenie tabel byłoby pozycyjne. Podobnie jak skrzyżowanie. – Nizam

+1

@SeanLange - Ansi SQL ma "INTERSECT ALL" i "EXCEPT ALL". –

Odpowiedz

5

W SQL Server, INTERSECT prace tylko na oddzielnych wierszach. Jeśli chcesz rozróżnić duplikaty wierszy, musisz rozróżnić wiersze. Jedyny sposób, aby to zrobić, to dodać kolejną kolumnę i zapełnić ją unikatowymi wartościami na duplikat, ale w taki sposób, aby wynikowe wiersze można było dopasować do różnych tabel.

Problem polega jednak na tym, że do tej pory nie ma uniwersalnej składni. Na przykład, możesz użyć ROW_NUMBER() do wyliczenia każdego duplikatu, ale będziesz musiał wypisać klauzulę PARTITION BY dla każdego przypadku indywidualnie: nie ma żadnego PARTITION BY *, a nie przynajmniej SQL Server.

W każdym razie, dla celów ilustracji, oto jak metoda ROW_NUMBER wyglądałby następująco:

SELECT 
    A, B, C, D, E, 
    ROW_NUMBER() OVER (PARTITION BY A, B, C, D, E ORDER BY (SELECT 1)) 
FROM 
    dbo.A 

INTERSECT 

SELECT 
    A, B, C, D, E, 
    ROW_NUMBER() OVER (PARTITION BY A, B, C, D, E ORDER BY (SELECT 1)) 
FROM 
    dbo.B 
; 

Jak napisano powyżej, zapytanie będzie również zwracać dodatkową kolumnę, kolumnę numer wiersza, na wyjściu .Jeśli chciał go powstrzymać, trzeba by dokonać kwerendy bardziej złożona:

SELECT 
    A, B, C, D, E 
FROM 
    (
    SELECT 
     A, B, C, D, E, 
     rn = ROW_NUMBER() OVER (PARTITION BY A, B, C, D, E ORDER BY (SELECT 1)) 
    FROM 
     dbo.A 

    INTERSECT 

    SELECT 
     A, B, C, D, E, 
     rn = ROW_NUMBER() OVER (PARTITION BY A, B, C, D, E ORDER BY (SELECT 1)) 
    FROM 
     dbo.B 
) AS s 
; 

I właśnie w celu wyjaśnienia, kiedy powiedziałem powyżej nie było składnia uniwersalne, miałem na myśli, że nie mógł tego zrobić bez uciekania się do dynamicznego SQL. Dzięki dynamicznemu SQL, wiele rzeczy jest możliwych, ale takie rozwiązanie byłoby o wiele bardziej skomplikowane i, moim zdaniem, znacznie mniej konserwowalne.

Ponownie, aby zilustrować punkt, jest przykładem tego, jak można go rozwiązać z dynamicznym SQL:

DECLARE 
    @table1 sysname, 
    @table2 sysname, 
    @columns nvarchar(max), 
    @sql nvarchar(max) 
; 

SET @table1 = 'dbo.A'; 
SET @table2 = 'dbo.B'; 

-- collecting the columns from one table only, 
-- assuming the structures of both tables are identical 
-- if the structures differ, declare and populate 
-- @columns1 and @columns2 separately 
SET @columns = STUFF(
    (
    SELECT 
     N', ' + QUOTENAME(name) 
    FROM 
     sys.columns 
    WHERE 
     object_id = OBJECT_ID(@table1) 
    FOR XML 
     PATH (''), TYPE 
).value('text()[1]', 'nvarchar(max)'), 
    1, 
    2, 
    '' 
); 

SET @sql = 
N'SELECT ' + @columns + N' 
FROM 
    (
    SELECT 
     ' + @columns + N', 
     ROW_NUMBER() OVER (PARTITION BY ' + @columns + N' ORDER BY (SELECT 1)) 
    FROM 
     ' + @table1 + N' 

    INTERSECT 

    SELECT 
     ' + @columns + N', 
     ROW_NUMBER() OVER (PARTITION BY ' + @columns + N' ORDER BY (SELECT 1)) 
    FROM 
     ' + @table2 + N' 
) AS s 
'; 

EXECUTE sp_executesql @sql; 

Zapewne widzisz teraz, co mam rozumieć przez „dużo bardziej skomplikowana” co najmniej.

+0

Dlaczego musiałeś przejść do dynamicznego sql? Przykład przecięcia z ROW_NUMBER powinien działać doskonale. Być może brakuje mi czegoś, ale dynamiczny sql nie wydaje się tu potrzebny. –

+0

@SeanLange: Tak, działa metoda ROW_NUMBER, to znaczy, że przy tej metodzie musisz wyraźnie podać wszystkie kolumny za każdym razem (w co najmniej PARTITION BY, jeśli wszystko jest w porządku z dodatkowymi kolumnami w wynikach), i to różne kolumny w różnych porównaniach. Dynamiczny SQL pozwala określić tylko nazwy tabel, a moim celem było pokazanie, przy którym koszcie (to znaczy, że samo rozwiązanie staje się dość skomplikowane). –

0
SELECT 
    COLUMN1 
FROM B 
WHERE B.COLUMN1 IN 
    (SELECT COLUMN1 
    FROM A) 
0
SELECT * FROM TableB 
WHERE EXISTS (SELECT 1 
       FROM TableA 
       WHERE ColumnName = TableB.ColumnName) 
+0

Bardzo podoba mi się ta odpowiedź, ale musiałbym znać kolumny łączące tabele i może ich być wiele. Jednym z rozwiązań w pobliżu jest 'wybierz A. * z A, gdzie istnieje (wybierz A. * przecinają wybieranie * z B)'. Co myślisz? – Nizam

+0

@Nizam, jeśli nie wiesz, ile kolumn będzie, to jest szansa, że ​​sposób, w jaki robisz coś jest nie tak – Steve

+0

Przecinają się tabele z różnymi wartościami w wierszach (biorąc pod uwagę wszystkie kolumny). W tym przypadku połączenie ma charakter pozycyjny. – Nizam