2014-09-18 14 views
5

C'è un modo per utilizzare l'intersezione senza selezionare solo valori distinti? Qualcosa come INTERSECT ALL.Intersect in SQL Server

Ad esempio, considerare tabella A e B

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

B --> 1, 1, 2 

comporterebbe

Result --> 1, 1, 2 

EDIT

Penso che questo link spiega bene quello che voglio. Questo other link è anche interessante per comprendere la domanda. O this other link spiega meglio l'evento.

EDIT 2

Supponiamo tabelle:

Tabella 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 ║ 
╚════════╩════╩═══╩════╩════╝ 

Tabella 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 ║ 
╚═════╩════╩═══╩════╩════╝ 

La risposta per Intersect (select * from A INTERSECT select * from B) sarebbe:

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

Perché richiede solo valori distinti. Quello che voglio sta prendendo file comuni, come:

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

Osservare Non ho bisogno di sapere quello che ho da collegare (la connessione è posizionale, proprio come INTERSECT). L'ID sarebbe qualcosa costruito usando tutte le colonne (il collegamento tra le tabelle sono tutte colonne, in base alla loro posizione).

+0

HUH? Perché il risultato ha due valori per 1? Questo non è ciò che INTERSECT significa affatto. È progettato per restituire valori distinti che sono presenti in entrambi. Avresti bisogno di qualcosa di diverso da intersecare affinché funzioni. –

+0

È un esempio. La connessione delle tabelle sarebbe posizionale. Proprio come l'intersezione. – Nizam

+1

@SeanLange - SQL ansi ha un 'INTERSECT ALL' e un' EXCEPT ALL'. –

risposta

5

In SQL Server, INTERSECT lavori solo su righe distinte. Se vuoi distinguere tra le righe duplicate, dovrai rendere le righe distinte. L'unico modo per farlo è quello di aggiungere un'altra colonna e popolarla con valori univoci per duplicato, ma in modo tale che le righe risultanti siano abbinabili tra diverse tabelle.

Il problema, tuttavia, è che finora non esiste una sintassi universale per questo. Ad esempio, è possibile utilizzare ROW_NUMBER() per enumerare ogni duplicato, ma è necessario scrivere la clausola PARTITION BY per ogni caso singolarmente: non esiste uno PARTITION BY *, non almeno in SQL Server.

In ogni caso, ai fini di illustrazione, ecco come il metodo ROW_NUMBER sarà simile:

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 
; 

Come scritto sopra, la query potrebbe anche restituire una colonna in più, la colonna numero di riga, in uscita .Se si voleva sopprimerla, si avrebbe bisogno di rendere la query più complessa:

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 
; 

E tanto per chiarire, quando ho detto sopra non c'era sintassi universale, volevo dire, non si potesse fare senza ricorrere alla dinamica SQL. Con SQL dinamico, sono possibili molte cose, ma una soluzione del genere sarebbe molto più complessa e, a mio parere, molto meno gestibile.

Anche in questo caso, per illustrare il punto, questo è un esempio di come si potrebbe risolvere con SQL dinamico:

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; 

Probabilmente si può vedere ora cosa intendevo per "molto più complessa" almeno.

+0

Perché hai dovuto andare a SQL dinamico? L'esempio intersect con ROW_NUMBER dovrebbe funzionare perfettamente. Forse mi manca qualcosa ma sql dinamico non sembra essere necessario qui. –

+0

@SeanLange: Sì, il metodo ROW_NUMBER funziona, è che con quel metodo è necessario specificare tutte le colonne esplicitamente ogni volta (in PARTITION BY almeno, se si sta bene con una colonna in più nell'output), e sono diverse colonne in diversi confronti. SQL dinamico consente di specificare solo i nomi delle tabelle e il mio punto era mostrare a quale costo (ad esempio la soluzione stessa diventa piuttosto complicata). –

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

Mi piace molto questa risposta, ma dovrei conoscere le colonne per collegare le tabelle e possono essere molte. Una soluzione vicino alla tua sarebbe 'selezionare A. * da A dove esiste (selezionare A. * intersect select * da B)'. Cosa ne pensi? – Nizam

+0

@Nizam se non sai quante colonne ci saranno, allora è probabile che il modo in cui stai facendo le cose sia sbagliato. – Steve

+0

L'intersezione prende tabelle con valori distinti in righe (considerando tutte le colonne). La connessione è posizionale in questo caso. – Nizam