2014-09-18 2 views
5

Есть ли способ использовать пересечение, не выбирая только отдельные значения? Что-то вроде INTERSECT ALL.Пересечь в SQL Server

Для примера рассмотрим таблицу А и В

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

B --> 1, 1, 2 

приведет ли

Result --> 1, 1, 2 

EDIT

Я думаю, что это link хорошо объясняет то, что я хочу. Это other link также интересует вопрос. Или this other link объясняет событие лучше.

EDIT 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). Идентификатор будет построен с использованием всех столбцов (связь между таблицей - все столбцы, основанные на их позиции).

+0

HUH? Почему результат имеет два значения для 1? Это не то, что означает INTERSECT. Он предназначен для возврата отдельных значений, которые присутствуют в обоих. Для этого вам понадобится нечто иное, чем пересечение. –

+0

Это пример. Соединение таблиц будет позиционным. Также как пересечение. – Nizam

+1

@SeanLange - Ansi SQL имеет 'INTERSECT ALL' и' EXCEPT ALL'. –

ответ

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, похоже, здесь не нужен. –

+0

@SeanLange: Да, метод ROW_NUMBER работает, это значит, что с помощью этого метода вам нужно будет указывать все столбцы явным образом каждый раз (в PARTITION BY по крайней мере, если вы в порядке с дополнительным столбцом на выходе) и это разные столбцы в разных сравнениях. Динамический SQL позволяет вам указывать только имена таблиц, и моя задача заключалась в том, чтобы показать, по какой цене (то есть, само решение становится довольно сложным). –

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

Мне очень нравится этот ответ, но мне нужно будет знать колонки для подключения таблиц, и их может быть много. Одним из решений рядом с вашим было бы «выбрать A. * из A где существует (выберите A. * intersect select * from B)'. Как вы думаете? – Nizam

+0

@Nizam, если вы не знаете, сколько столбцов будет тогда, вероятность того, что вы делаете что-то неправильно – Steve

+0

Пересечение принимает таблицы с различными значениями в строках (учитывая все столбцы). В этом случае соединение является позиционным. – Nizam

Смежные вопросы