Кажется, я столкнулся с некоторой дилеммой о том, как наилучшим образом решить это требование. Я понимаю, что этот вопрос очень тесно связан со следующими другими вопросами:ПОЛНАЯ ВНЕШНЯЯ ВСТУПЛЕНИЕ с отфильтрованными данными
- Problem with Full Outer Join not working as expected
- What is the difference when adding a filter criteria to an outer join instead of a where clause?
- Oracle outer join not working as expected
- и, вероятно, многие другие ...
Добавленный вопрос заключается в том, что Интересно, какое общее мнение касается того, как решить эту проблему.
IF OBJECT_ID('tempdb..#A') IS NOT NULL DROP TABLE #A
IF OBJECT_ID('tempdb..#B') IS NOT NULL DROP TABLE #B
GO
CREATE TABLE #A (key1 int NOT NULL PRIMARY KEY,
value1 int NOT NULL,
value2 int NOT NULL,
is_even AS (CASE WHEN key1 % 2 = 0 THEN 1 ELSE 0 END))
CREATE TABLE #B (key1 int NOT NULL PRIMARY KEY,
value1 int NOT NULL,
value2 int NOT NULL,
is_even AS (CASE WHEN key1 % 2 = 0 THEN 1 ELSE 0 END))
GO
-- dummy data
INSERT #A (key1, value1, value2)
SELECT TOP 10 key1 = ROW_NUMBER() OVER (ORDER BY x1.object_id),
value1 = ROW_NUMBER() OVER (ORDER BY x1.object_id) % 7,
value2 = ROW_NUMBER() OVER (ORDER BY x1.object_id) % 5
FROM master.sys.objects x1, master.sys.objects x2, master.sys.objects x3
INSERT #B (key1, value1, value2)
SELECT key1, value1, value2
FROM #A
GO
-- create holes but keep SOME overlap
DELETE #A WHERE value1 > value2 -- removes 3 records
DELETE #B WHERE value1 < value2 -- removes 3 records
GO
-- show effect on tables
--SELECT * FROM #A ORDER BY key1
--SELECT * FROM #B ORDER BY key1
GO
-- create complete overview
SELECT key1 = ISNULL(a.key1, b.key1),
value1a = a.value1, value2a = a.value2,
value1b = b.value1, value2b = b.value2
FROM #A a
FULL OUTER JOIN #B b
ON b.key1 = a.key1
ORDER BY 1
GO
-- what if we only want the even records
-- THIS DOES NOT WORK !
SELECT key1 = ISNULL(a.key1, b.key1),
value1a = a.value1, value2a = a.value2,
value1b = b.value1, value2b = b.value2
FROM #A a
FULL OUTER JOIN #B b
ON b.key1 = a.key1
AND b.is_even = 1
WHERE a.is_even = 1
ORDER BY 1
Я знаю, почему это не работает; Мне просто интересно, какой будет самый четкий подход, чтобы заставить его работать и оставаться читаемым для других людей. Бонусные баллы, если они также работают на системах, отличных от MSSQL.
"Мое" решение до сих пор является:
, ловя NULL из-за КОСМИЧЕСКИЙ эффект:
SELECT key1 = ISNULL(a.key1, b.key1),
value1a = a.value1, value2a = a.value2,
value1b = b.value1, value2b = b.value2
FROM #A a
FULL OUTER JOIN #B b
ON b.key1 = a.key1
WHERE ISNULL(a.is_even, b.is_even) = 1
ORDER BY 1
С помощью КТРА
;WITH a (key1, value1, value2)
AS (SELECT key1, value1, value2
FROM #A
WHERE is_even = 1),
b (key1, value1, value2)
AS (SELECT key1, value1, value2
FROM #B
WHERE is_even = 1)
SELECT key1 = ISNULL(a.key1, b.key1),
value1a = a.value1, value2a = a.value2,
value1b = b.value1, value2b = b.value2
FROM a
FULL OUTER JOIN b
ON b.key1 = a.key1
ORDER BY 1
С помощью подзапросов
SELECT key1 = ISNULL(a.key1, b.key1),
value1a = a.value1, value2a = a.value2,
value1b = b.value1, value2b = b.value2
FROM (SELECT key1, value1, value2
FROM #A
WHERE is_even = 1) a
FULL OUTER JOIN (SELECT key1, value1, value2
FROM #B
WHERE is_even = 1) b
ON b.key1 = a.key1
ORDER BY 1
Al хотя я предпочитаю первое решение, решения CTE и/или подзапросов выглядят более очевидными, даже если они добавляют в код много пуха. (И мне не очень нравится CTE =)
Любые мнения? Другие решения? Замечания (например, касательно производительности по «реальным» данным)
Я обнаружил, что фильтрация снаружи полного внешнего соединения очень сложна и подвержена ошибкам. Семантика NULL противоречит интуиции. По этой причине я всегда фильтруюсь внутри. Это также кажется более логичным для меня. – usr