2014-09-18 51 views
5

有沒有一種方法可以在不選擇不同值的情況下使用相交?像INTERSECT ALL在SQL Server中相交

例如,考慮表A和B

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

B --> 1, 1, 2 

會導致

Result --> 1, 1, 2 

編輯

我覺得這link很好地解釋了我想要的東西。這other link也有意思瞭解這個問題。或者this other link更好地解釋事件。

編輯2

假設表:

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

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

爲交叉(select * from A INTERSECT select * from B)答案將是:

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

因爲它只需要不同的值。我想正在共同行,就像:

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

觀察我不需要知道我必須連接(連接位置,就像INTERSECT)。該ID將是使用所有列構建的東西(表格之間的鏈接都是基於其位置的列)。

+0

HUH?爲什麼結果對於1有兩個值?這不是INTERSECT的意思。它旨在返回兩者中存在的不同值。你需要的東西不是相交才能工作。 – 2014-09-18 20:34:14

+0

這是一個例子。表格的連接是位置的。就像十字路口。 – Nizam 2014-09-18 20:58:31

+1

@SeanLange - Ansi SQL有一個'INTERSECT ALL'和一個'EXCEPT ALL'。 – 2014-09-19 08:03:24

回答

5

在SQL Server中,INTERSECT作品只不同的行。如果你想讓它區分重複的行,你需要使行不同。唯一可以這樣做的方法是我可以考慮添加另一列,並使用每個副本的唯一值來填充它,但這樣可以使得結果行在不同表中匹配。

但是,問題是,到目前爲止,沒有通用的語法。例如,可以使用ROW_NUMBER()來枚舉每個重複項,但是您必須爲每個個案分別寫出它的PARTITION BY子句:沒有PARTITION BY *,至少沒有在SQL Server中。

總之,爲了說明的目的,這裏是如何的ROW_NUMBER方法看起來像:

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 
; 

如上面所寫,查詢也將返回一個額外的列,行號列,在輸出。如果你想抑制它,你需要使查詢更加複雜:

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 
; 

而只是爲了澄清,我上面說的有沒有通用的語法,我的意思是,你不能這樣做,而不訴諸動態SQL。使用動態SQL,很多事情都是可能的,但這樣的解決方案會更加複雜,並且在我看來,維護起來要少得多。

同樣,爲了說明這一點,這是一個如何,你可以使用動態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; 

現在可以了我的意思可能看到「要複雜得多」至少。

+0

爲什麼你必須去動態SQL?與ROW_NUMBER交叉的例子應該工作得很好。也許我錯過了一些東西,但動態SQL似乎並不需要在這裏。 – 2014-09-19 14:43:43

+0

@SeanLange:是​​的,ROW_NUMBER方法起作用,就是用這種方法你需要每次明確指定所有的列(至少在PARTITION BY中,如果你在輸出中有一個額外的列是好的),並且它是不同比較中的不同列。動態SQL允許你指定表名,我的觀點是要顯示哪種代價(即解決方案本身變得相當複雜)。 – 2014-09-19 15:02:51

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

我很喜歡這個答案,但我必須知道連接表的列,而且它們可以很多。你附近的一個解決方案是'select A * from A where exists(select A. * intersect select * from B)'。你怎麼看? – Nizam 2014-09-18 20:48:14

+0

@Nizam如果你不知道有多少列會出現,那麼機會就在於你做事的方式是錯誤的 – Steve 2014-09-18 20:51:52

+0

交叉會在行中使用不同值的表(考慮所有列)。在這種情況下連接是位置的。 – Nizam 2014-09-18 20:57:19