Пренаписване на заявки JOIN като EXISTS

Опитвам се да разбера пренаписването на заявки от EXISTS като JOIN и обратно.

Така че имам това:

SQL Fiddle

Настройка на схема на Oracle 11g R2:

create table store
(
  storeKey number,
  storeName varchar2(500),
  storeLocationKey number,
  constraint StorePK primary key(storeKey)
);

create table storeLocation
(
  storeLocationKey number,
  storeLocationName varchar2(500),
  storeCountry varchar2(500),
  constraint StoreLocPK primary key(storeLocationKey)
);

insert into store values(1, 'Le Store', 1);
insert into store values(2, 'La tiendinha', 2);
insert into store values(3, 'The SuperHyperMegaStore', 3);
insert into store values(4, 'Le Other Store', 1);
insert into store values(5, 'La tienda', 4);
insert into store values(6, 'Chiquinha Tienda', 2);
insert into store values(7, 'Pecorela Tiendinha', 3);
insert into store values(8, 'Le Petit Store', 1);
insert into store values(9, 'Tienda Cipote', 4);
insert into store values(10, 'Tienda Desconocida', 0);


insert into storeLocation values(1, 'Camps Elisees', 'France');
insert into storeLocation values(2, 'Brasilia', 'Brasil');
insert into storeLocation values(3, 'Boston', 'USA');
insert into storeLocation values(4, 'San Salvador', 'El Salvador');

Заявка 1:

SELECT store.*
FROM store
LEFT OUTER JOIN storeLocation
    ON store.storeLocationKey = storeLocation.storeLocationKey
WHERE storeLocation.storeCountry <> 'France'
ORDER BY store.storeKey ASC

Резултати:

| STOREKEY |               STORENAME | STORELOCATIONKEY |
|----------|-------------------------|------------------|
|        2 |            La tiendinha |                2 |
|        3 | The SuperHyperMegaStore |                3 |
|        5 |               La tienda |                4 |
|        6 |        Chiquinha Tienda |                2 |
|        7 |      Pecorela Tiendinha |                3 |
|        9 |           Tienda Cipote |                4 |

Заявка 2:

SELECT *
FROM store
WHERE EXISTS (
        SELECT 1
        FROM storeLocation
        WHERE storeLocationKey = store.storeLocationKey
            AND storeCountry <> 'France'
        )
ORDER BY storeKey ASC

Резултати:

| STOREKEY |               STORENAME | STORELOCATIONKEY |
|----------|-------------------------|------------------|
|        2 |            La tiendinha |                2 |
|        3 | The SuperHyperMegaStore |                3 |
|        5 |               La tienda |                4 |
|        6 |        Chiquinha Tienda |                2 |
|        7 |      Pecorela Tiendinha |                3 |
|        9 |           Tienda Cipote |                4 |

Заявка 3:

-----------------------------

SELECT store.*
FROM store
LEFT OUTER JOIN storeLocation
    ON store.storeLocationKey = storeLocation.storeLocationKey
where storeLocation.storeLocationName is null
ORDER BY store.storeKey ASC

Резултати:

| STOREKEY |          STORENAME | STORELOCATIONKEY |
|----------|--------------------|------------------|
|       10 | Tienda Desconocida |                0 |

Заявка 4:

SELECT store.*
FROM store
WHERE NOT EXISTS (
        SELECT NULL
        FROM storeLocation
        WHERE storeLocationKey = store.storeLocationKey
        )

Резултати:

| STOREKEY |          STORENAME | STORELOCATIONKEY |
|----------|--------------------|------------------|
|       10 | Tienda Desconocida |                0 |

от тук имам няколко (тъпи) въпроса:

  • Защо Заявки 2 и 4 трябва да бъдат свързани (когато не корелирах заявката, тя не върна нищо)? Съществува ли/не съществува ли трябва да бъде свързано, за да работи?

  • Какви са сценариите, при които е по-добре да работите с някой от тях?

  • Има ли разлика при работа с голям набор от данни (DW)?

Благодаря.


person StrayChild01    schedule 05.08.2014    source източник


Отговори (1)


Въпрос 1:

Всъщност, ако не свържете вашата подзаявка, вашата заявка 2 ще върне всеки ред извън Франция (SQLFiddle). Това е така, защото корелираната заявка: „Върни редове от магазин, където има поне един съвпадащ ред в местоположението на магазина, което не е във Франция“. Вашата несвързана заявка означава „Върни редове от магазин, където има поне ред в местоположението на магазина, което не е във Франция“. Тъй като подзаявката винаги ще връща поне един ред, условието WHERE винаги е вярно.

В случай на заявка 4, без корелация, подзаявката винаги ще връща ред, условието WHERE винаги ще бъде невярно и няма да получавате редове.

Въпрос 2:

Ако бях аз, тъй като не избирате нищо от storelocation, бих използвал версията EXISTS. Изглежда ми по-ясно, че единственото нещо, което ви интересува, е дали има или не съответстващ ред в storelocation, отколкото в случая на присъединяване.

Това е достатъчно проста заявка, за която подозирам, че оптимизаторът ще излезе със същия план за изпълнение, но не знам това със сигурност.

Също така, виждал съм случаи, в които клаузата EXISTS позволява на оптимизатора да спре да търси редове, след като намери съвпадение, вместо да се налага да прави пълното присъединяване.

Очевидно няма да го направите в този случай, но ако местоположението на вашия магазин има множество местоположения с един и същ идентификатор, броят на редовете в двата случая ще бъде различен.

Въпрос 3:

Наистина няма начин да се отговори на този въпрос, без да се види план за изпълнение. Бих написал вашето запитване по начин, който прави най-ясно на читателя какво се опитвате да направите, след което ще го тествам. Ако времето, необходимо за бягане, е приемливо, спрете. Само ако е твърде бавен, трябва да се опитате да го накарате да работи бързо.

person eaolson    schedule 05.08.2014
comment
Благодаря за изяснителния отговор, @eaolson. И така, основно: ако EXISTS като поне връщащ се ред, външната заявка ще върне стойност, а NOT EXISTS няма да върне никаква стойност, ако стойността е върната при вътрешна заявка, нали? За номер 2 обаче имам въпрос: Как бихте могли да изберете нещо от таблицата storeLocation, ако то е вътре в съществува? Ще трябва ли да се присъедините към него? Благодаря отново. - person StrayChild01; 05.08.2014