ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ с отфильтрованными данными

Кажется, я столкнулся с дилеммой о том, как лучше всего решить это требование. Я понимаю, что проблема очень тесно связана со следующими другими вопросами:

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

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

Хотя я предпочитаю первое решение, решения CTE и/или подзапроса выглядят более очевидными, даже несмотря на то, что они добавляют МНОГО лишнего в код. (И я не очень люблю CTE =)

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


person deroby    schedule 29.11.2013    source источник
comment
Я обнаружил, что фильтрация вне полного внешнего соединения очень сложна и подвержена ошибкам. Семантика NULL нелогична. По этой причине я всегда фильтрую внутри. Мне тоже это кажется более логичным.   -  person usr    schedule 01.12.2013


Ответы (1)


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

Все 3 запроса имеют одинаковую расчетную стоимость и одинаковый ввод-вывод:

введите здесь описание изображения

Таблица '#B'. Счетчик сканирований 1, логических операций чтения 2, физических операций чтения 0, операций упреждающего чтения 0, логических операций чтения 0 объектов, физических операций чтения 0 объектов, операций упреждающего чтения 0 объектов.

Таблица '#A'. Счетчик сканирований 1, логических операций чтения 2, физических операций чтения 0, операций упреждающего чтения 0, логических операций чтения 0 объектов, физических операций чтения 0 объектов, операций упреждающего чтения 0 объектов.

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

введите здесь описание изображения

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

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