2013-10-09 3 views
0

У меня есть две таблицы в той же базе данных sql-сервера, которая хранит имена приложений и имена пользователей (между прочим) из двух последовательных кварталов. То, что я хочу, - это полное соединение, которое показывает пользователей в таблице A, которые не указаны в таблице B и наоборот.Полное нарушение внешнего соединения

EDIT: И table_a, и table_b содержат две или более записи, в которых имена приложений и имя пользователя имеют одинаковые значения, что делает необходимым DISTINCT. Я отредактировал приведенные ниже примеры, чтобы отразить это.

TABLE_A:

----------------------- 
| AppName | Username | 
----------------------- 
| app1  | jdoe  | 
| app1  | jsmith | 
| app1  | jdoe  | 
| app2  | jsmith | 
----------------------- 

TABLE_B:

----------------------- 
| AppName | Username | 
----------------------- 
| app1  | fbar  | 
| app1  | jsmith | 
| app1  | jboehner | 
| app1  | fbar  | 
| app3  | jboehner | 
----------------------- 

SQL:

SELECT DISTINCT a.username as q2, b.username as q3 
FROM tablea as a 
FULL JOIN tableb as b 
ON a.username = b.username 
WHERE a.appname = 'app1' 
AND b.appname = 'app1' 

Ожидаемый результат:

----------------------- 
| q2  | q3  | 
----------------------- 
| jdoe  |   | 
| jsmith | jsmith | 
|   | fbar  | 
----------------------- 
|   | jboehner | 
----------------------- 

Фактический результат:

----------------------- 
| q2  | q3  | 
----------------------- 
| jsmith | jsmith | 
----------------------- 

Что дает?

ответ

4

Используя условие WHERE на внешней присоединяемой таблицы (в данном случае, как из ваших таблиц) изменяет его внутреннее соединение с фильтром применяется к результату объединения и должны быть выполнены все условия. В вашем случае это невозможно, кроме как в строках, которые соответствуют (он заставляет его ВХОДИТЬ JOIN). Я подозреваю, что это то, что вы имели в виду:

SELECT DISTINCT a.username as q2, b.username as q3 
FROM dbo.tablea as a 
FULL OUTER JOIN dbo.tableb as b 
ON a.username = b.username 
AND (a.appname = b.appname) 
WHERE a.appname = 'app1' OR b.appname = 'app1'; 

-- or 

SELECT DISTINCT a.username as q2, b.username as q3 
FROM dbo.tablea as a 
FULL OUTER JOIN dbo.tableb as b 
ON a.username = b.username 
AND (a.appname = b.appname) 
WHERE 'app1' IN (a.appname, b.appname); 
+0

Не могли вы ПОЛНАЯ OUTER JOIN на a.username = b.username И a.appname = b.appname, а затем положить критерии WHERE постфактум, поскольку вы» ve уже создал набор данных с NULL в соответствующих местах? –

+1

Будет ли SQL Server иметь возможность подталкивать фильтр в отдельные таблицы таким образом? Я использую для ручного нажатия на фильтры в производные таблицы. – usr

+0

@ Love2Learn да, но важная часть заключается в том, что предложение WHERE не может иметь AND для обеих таблиц (это делает его INNER JOIN). –

-3

где просто должно быть или нет, и: WHERE a.appname = 'app1' OR b.appname = 'app1'

+3

Нет, это позволит любой таблице вводить строки, где 'appname <> 'app1''. –

3
SELECT DISTINCT a.username as q2, b.username as q3 
FROM (SELECT x.* FROM tablea x WHERE x.appname = 'app1') as a 
FULL JOIN (SELECT y.* FROM tableb y WHERE y.appname = 'app1') as b 
ON a.username = b.username 

Редактировать # 1:

USE AdventureWorks2008R2; 
SET STATISTICS IO ON; 
SET NOCOUNT ON; 

PRINT 'Test #1 ' 
PRINT '{' 
SELECT DISTINCT x.Name, y.Name 
FROM 
(
    SELECT p.* 
    FROM Production.Product p 
    WHERE p.Name = 'HL Road Frame' 
) x 
FULL OUTER JOIN 
(
    SELECT pm.* 
    FROM Production.ProductModel pm 
    WHERE pm.Name = 'HL Road Frame' 
) y ON x.ProductModelID = y.ProductModelID 
PRINT '}' 
GO 

PRINT 'Test #2 ' 
PRINT '{' 
SELECT DISTINCT p.Name, pm.Name 
FROM Production.Product p 
FULL OUTER JOIN Production.ProductModel pm 
ON p.ProductModelID = pm.ProductModelID 
AND (p.Name = pm.Name) 
WHERE p.Name = 'HL Road Frame' OR pm.Name = 'HL Road Frame' 
PRINT '}' 
GO 

SET STATISTICS IO OFF; 

Edit # 2:

Test #1 
{ 
Table 'ProductModel'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Product'. Scan count 0, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
} 
Test #2 
{ 
Table 'ProductModel'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Product'. Scan count 1, logical reads 15, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
} 

Edit # 3:

enter image description here

+1

@AaronBertrand, если таблица проиндексирована на имя приложения, это может привести к очень эффективному плану. При правильных индексах это может привести к объединению без сортировки.На самом деле я не вижу, как запрос может стать быстрее, если фильтр перемещается наружу. – usr

+0

Мне интересно. Подумайте, используя пастебин. – usr

+0

@AaronBertrand, как насчет индекса на (имя приложения, имя пользователя), включают (*)? Включение должно быть необязательным, если фильтр в appname очень избирателен, а таблицы большие. – usr

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