2013-11-29 7 views
5

Кажется, я столкнулся с некоторой дилеммой о том, как наилучшим образом решить это требование. Я понимаю, что этот вопрос очень тесно связан со следующими другими вопросами:ПОЛНАЯ ВНЕШНЯЯ ВСТУПЛЕНИЕ с отфильтрованными данными

Добавленный вопрос заключается в том, что Интересно, какое общее мнение касается того, как решить эту проблему.

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 =)

Любые мнения? Другие решения? Замечания (например, касательно производительности по «реальным» данным)

+0

Я обнаружил, что фильтрация снаружи полного внешнего соединения очень сложна и подвержена ошибкам. Семантика NULL противоречит интуиции. По этой причине я всегда фильтруюсь внутри. Это также кажется более логичным для меня. – usr

ответ

4

Ваши два подхода «С CTE» и «с подзапросами» - это точно то же самое, это личное предпочтение, которое вы будете использовать.

Все 3 запросов имеют одинаковую расчетную стоимость и тот же I/O:

enter image description here

Таблица '#B'. Число сканирования 1, логическое считывание 2, физическое чтение 0, чтение вперед 0, логическое считывание 0, логическое чтение 0, чтение с чтением 0, считывание с чтением 0.

Таблица «#A». Количество сканирования 1, логическое чтение 2, физические чтения 0, упреждающее чтение читает 0, нескладные логические читает 0, нескладные физические чтения 0, подбросить упреждающее чтение читают 0.

Однако первые имеет дополнительный шаг Filter, потому что подзапрос/CTE-подход способен применять предикат is_even = 1 одновременно с сканированием кластерного индекса.

enter image description here

Так что я пошел бы для любого подхода подзапроса или подхода КТР в зависимости от которого вы предпочитаете визуально. Не обманывайте себя, думая меньше, всегда меньше, когда дело доходит до SQL, более эффективно писать более подробные запросы.

+0

По иронии судьбы, большую часть времени я всегда говорю, что «меньший» код не обязательно означает «более быстрый» код =) Thx для вашего ответа; Я в значительной степени убежден здесь, хотя все еще немного надеялся, что кто-то перепутает что-то «радикально другое, но болезненно простое», о котором я не думал (пока) ... (человек может мечтать =) – deroby

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