* Join или вложен Select?
Публикувано на 02 април 2011 в раздел Бази от Данни.
Въпросът поставен в заглавието на статията е много често разискван и около него се водят спорове. По принцип има една тенденция програмистите категорично да избягват вложените select заявки, защото още от миналото има един мит, че те се изпълняват бавно. Този мит се дължи главно на грешки в СУБД, които не са използвали правилно индексите при вложените заявки. Днес това отдавна вече (почти) не се среща, т.е. можем да очакваме вложените заявки да вървят достатъчно добре. Така въпросът “join или вложен select” отново стои на дневен ред.
Ще демонстрираме с един пример. Нека имаме следната база от данни:
CREATE TABLE clients(
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL) ENGINE=InnoDB;
CREATE TABLE products(
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL) ENGINE=InnoDB;
CREATE TABLE orders(
client_id INT UNSIGNED NOT NULL,
product_id INT UNSIGNED NOT NULL,
quantity INT UNSIGNED NOT NULL,
PRIMARY KEY(client_id,product_id),
FOREIGN KEY (client_id) REFERENCES clients(id),
FOREIGN KEY (product_id) REFERENCES products(id)
) ENGINE=InnoDB;
INSERT INTO clients(id, name)
VALUES (NULL, "Ivan"),
(NULL, "Petar"),
(NULL, "Maria"),
(NULL, "Philip");
INSERT INTO products(id, name)
VALUES (NULL, "GSM"),
(NULL, "Bike"),
(NULL, "Icecream");
INSERT INTO orders(client_id, product_id, quantity)
VALUES (1, 1, 2),
(1, 3, 4),
(2, 1, 1),
(2, 2, 1),
(2, 3, 2),
(3, 2, 2),
(4, 3, 1);
Поставяме си да решим следната задача: изведете id-тата на хората, които са поръчали продукти 1 или 2. Впечатление прави условието “или”, както също така, че връзката между clients и products е от тип M:M.
Първо ще дадем естественото решение с вложен select:
SELECT clients.name
FROM clients
WHERE clients.id IN(
SELECT orders.client_id
FROM orders
WHERE product_id IN (1,2)
);
Очаквано отговорът на СУБД е:
+-------+ | name | +-------+ | Ivan | | Petar | | Maria | +-------+
При използването на решение с join положението не е толкова просто. Следният пример е грешен:
SELECT clients.name FROM clients JOIN orders ON clients.id=orders.client_id WHERE orders.product_id IN (1,2);
Ще видим, че в резултатът от него има повторения:
+-------+ | name | +-------+ | Ivan | | Petar | | Petar | | Maria | +-------+
За да се избавим от повторенията ще е нужно да използваме ключова дума DISTINCT:
SELECT DISTINCT clients.name FROM clients JOIN orders ON clients.id=orders.client_id WHERE orders.product_id IN (1,2);
Така заявката вече ще върне правилен резултат.
Коя от двете заявки беше по-добра? Отговорът еднозначно е, че това е тази, която е с вложен select! При заявката с join първо се генерира таблицата с повторенията, а чак след това се премахнаха дублиращите се. При вложен select дублирания въобще нямаше. Ако направите по-сериозен тест с огромно количество данни с много повторения и създадени индекси по съответните колони, то ще видите, че заявката с вложен select ще се изпълни видимо по-бързо.
Когато използвате SELECT DISTINCT … WHERE… заявки (или GROUP BY), то СУБД първо ще създаде временна свързваща таблица и чак тогава ще ги агрегира. При вложен select това не се случва, съответно не се правят безсмислени операции.
Ето още един начин да направим същата заявка, но вместо IN ще използваме EXISTS:
SELECT clients.name
FROM clients
WHERE EXISTS(
SELECT * FROM orders
WHERE orders.product_id IN (1,2) AND orders.client_id=clients.id
);
Дали IN или EXISTS е по-добър подход ще разгледаме по-късно в отделна статия. При всички положения обаче вложения select би трябвало да дава по-добри резултати от използването на join като негова алтернатива.
2 коментара за “Join или вложен Select?”
Trackback URI | RSS за коментарите
Пусни коментар
Категории
- Бази от Данни (52)
- Вероятности (31)
- История (15)
- Кучета (69)
- Лада Нива (96)
- Математика (166)
- Методика (53)
- Общи работи (110)
- ПИК-3 Java (38)
- Политика (41)
- Програмни Среди (1)
- ПТСК (41)
- С/C++ (45)
- Семейни (16)
- Физика (35)
- ХHTML/JS (25)
- Храна (11)
Нови
- Извеждане на няколко произволни реда
- Full-Text търсене с InnoDB в MySQL
- Късметче от кафе
- Пред блока…
- Бушонно табло на Лада Нива
02 април 2011 на 22:56
Благодаря за информацията.
За мене е много полезно уточнение, защото винаги съм се чудел, как е “по-правилно” да се пише.
01 септември 2011 на 17:07
а така мисля, че е по добре: