Операторите за набор са SQL операторите, които се занимават с комбиниране по различни начини на различни набори от резултати. Да речем, че имате два различни SELECT s, които искате да комбинирате в един набор от резултати, операторите за набор влизат в игра. MariaDB поддържа UNION и UNION ALL set оператори за дълго време и това са най-често срещаните SQL оператори за набор.
Но тук се изпреварваме, нека първо да обясня операторите на SQL набор, които имаме и как работят. Ако искате да опитате това, можете да използвате съществуващото си внедряване на MariaDB Server или да изпробвате това в облачна база данни на MariaDB SkySQL.
UNION и UNION ALL
UNION и UNION ALL операторите за набор добавят резултата от два или повече набора от резултати. Нека започнем с UNION ALL и UNION тогава ще бъде вариант на UNION ALL .

Нека да разгледаме как изглежда в SQL. Да предположим, че управляваме уеб магазин и че за продуктите, които продаваме, имаме инвентар. Сега искаме да видим всички продукти, които са по поръчка или са в инвентара, заявка за това ще изглежда така:
ИЗБЕРЕТЕ oi.prod_id, p.prod_name ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id UNION ВСИЧКИ ИЗБЕРЕТЕ i.prod_id, p.prod_name ОТ инвентара i ПРИСЪЕДИНЕТЕ продукти p ON i.prod_id =p.id;
В теорията на множеството това е UNION на комплектите продукти, които са поръчани и комплектите продукти, които са в инвентара. Което е добре на теория, но има проблем с резултата от тази заявка. Проблемът е, че продукт, който се появява както в поръчките, така и в инвентара, или на множество места в инвентара, ще се появи повече от веднъж в изхода. Този проблем е причината UNION ALL не се използва много и вместо това UNION DISTINCT (DISTINCT е по подразбиране и може да се игнорира). Например:
ИЗБЕРЕТЕ oi.prod_id, p.prod_name ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id UNION ИЗБЕРЕТЕ i.prod_id, p.prod_name ОТ инвентара i ПРИСЪЕДИНЕТЕ продукти p ON i.prod_id =p.id;предварително>С тази заявка продукт, който е по поръчка или съществува в инвентара, е посочен само веднъж. Имайте предвид, че когато премахваме дубликати тук, стойностите се сравняват, така че два реда с еднакви стойности в една и съща колона се считат за равни, въпреки че стойностите идват от различни таблици или колони.
За да бъда честен обаче, няма нищо в заявката по-горе, което да не може да се направи с обикновен
SELECTот продуктите маса и няколко съединения. В известен смисълUNIONможе да е по-лесно за четене. От друга страна, ако искаме да имаме списък с продукти по поръчка или в инвентара и също така искаме да знаем кой е бил, тогава заявката ще бъде нещо подобно:ИЗБЕРЕТЕ 'По поръчка', oi.prod_id, p.prod_name FROM order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id UNION ИЗБЕРЕТЕ 'Inventory', i.prod_id, p.prod_name ОТ инвентар i ПРИСЪЕДИНЕТЕ продукти p ON i.prod_id =p.id;Ето заявка, която не е лесна за изпълнение с обикновен
SELECTот продуктите таблица, тъй като разглеждаме един и същ ред от таблицата с продукти два пъти (веднъж за order_items и веднъж за инвентара ).Още оператори за SQL набор
С MariaDB Server 10.3 дойдоха два нови оператора за SQL набор, въведени до голяма степен за подобряване на съвместимостта с Oracle, но тези оператори са полезни сами по себе си. След това MariaDB Server 10.4 добавя възможността за контрол на приоритета на оператора. Ще разгледаме и това. Без възможност за контрол на приоритета на оператора, зададените оператори не винаги работят, както бихте искали или очаквате.
Новите оператори за SQL набор са
INTERSECTиEXCEPTи те са полезни, особено при използване на анализи. Също така, въпреки чеJOINs и други конструкции често могат да се използват вместо това, SQL операторите за набор позволяват SQL синтаксис, който може да бъде по-лесен за четене и разбиране. И ако имате приложения на Oracle, които мигрирате към MariaDB, полезността на тези оператори е очевидна.
Операторът за набор INTERSECT
INTERSECTоператорът ще върне всички елементи, които съществуват в два или повече набора, или в SQL термини, всички редове, които съществуват в два набора от резултати. В този случай се създава напречно сечение на двата комплекта елементи. В термините на SQL това означава, че се връщат само редове, които съществуват и в двата набора, така че ако искам да проверя кои продукти имам по поръчка и кои също са в инвентара, една заявка може да изглежда така:ИЗБЕРЕТЕ oi.prod_id, p.prod_name ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id INTERSECT SELECT i.prod_id, p.prod_name ОТ инвентара i ПРИСЪЕДИНЕТЕ се продукти p ON i.prod_id =p.id;предварително>Отново тази заявка може да бъде конструирана с помощта на
JOINна продуктите таблица, но заявката по-горе е малко по-ясна за това, което се опитваме да постигнем.Операторът за набор EXCEPT
В случай на
EXCEPTоператор, искаме елементите, които са в един от наборите, но не и в другия. Така че, отново използвайки примера по-горе, ако искаме да видим продуктите, които имаме по поръчка, но за които нямаме инвентар, можем да напишем заявка като тази:ИЗБЕРЕТЕ oi.prod_id, p.prod_name ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id ОСВЕН ИЗБЕРЕТЕ i.prod_id, p.prod_name ОТ инвентара i ПРИСЪЕДИНЕТЕ се продукти p ON i.prod_id =p.id;предварително>Отново има други начини за писане на тази конкретна заявка, но за други, по-разширени заявки, когато комбинираме данни от две различни таблици, това не е така.
Комбиниране на множество оператори за набор
Можете да комбинирате повече от 2 набора оператора, ако това е полезно. Например, нека видим дали можем да намерим продукти, които са по поръчка и са доставени или са на склад. SQL за това би изглеждал така:
ИЗБЕРЕТЕ oi.prod_id, p.prod_name ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name ОТ доставки d ПРИСЪЕДИНЕТЕ продукти p ON d.prod_id =p.id UNION SELECT i.prod_id, p.prod_name ОТ инвентара i ПРИСЪЕДИНЕТЕ се към продукти p ON i.prod_id =p.id;За да изразя това на разбираем език, това, което се случва е, че първо проверявам кои продукти са поръчани и кои са доставени, а след това комбинирам този набор от продукти с всички продукти в инвентара. Всеки продукт, който не е в набора от резултати, не е в инвентара но може да е по поръчка или да е доставено, но не и двете.
Но сега нека изразим това по различен начин и да видим какво ще се случи. Искам списък на всички продукти, които са на склад или са доставени и са по поръчка. Тогава SQL ще бъде нещо подобно, подобно на SQL по-горе, но малко по-различно:
ИЗБЕРЕТЕ i.prod_id, p.prod_name ОТ инвентара i JOIN продукти p ON i.prod_id =p.id UNION SELECT oi.prod_id, p.prod_name ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id INTERSECT SELECT SELECT SELECT. d.prod_id, p.prod_name ОТ доставки d ПРИСЪЕДИНЕТЕ се към продукти p ON d.prod_id =p.id;Как тълкувате това тогава? Изброявате ли продукти, които са на склад и които са по поръчка и продуктите, които се доставят? Ето как изглежда това, нали? Просто това е
INTERSECT(иEXCEPTпо този въпрос) има приоритет презUNION. Двата SQL оператора произвеждат един и същ набор от резултати, поне в MariaDB и ето как SQL стандартът казва, че нещата трябва да работят. Но има изключение, Oracle.Как работи това в Oracle
Oracle има и четирите оператора на набора SQL (
UNION,UNION ALL,INTERSECTиEXCEPT) за дълго време, много преди да бъдат стандартизирани, така че изпълнението им е малко по-различно. Нека опитаме с горните таблици и да вмъкнем някои данни в тях. Данните са много прости и отразяват не толкова успешна компания, но работят като пример и тук показваме само съответните колони.
| Таблица | продукти | поръчкови_артикули | инвентар | доставки | ||
| Колона | prod_id | име на продукта | идентификатор_на_поръчка | prod_id | prod_id | prod_id |
| Данни | 1 | Синя ваза | 1 | 1 | 1 | 2 |
| 2 | Червена ваза | 2 | 1 | 2 | 3 | |
| 3 | Червен килим | 2 | 3 | |||
С наличните данни, нека отново да разгледаме последния SQL израз по-горе. Има функция, която ви позволява да контролирате приоритета и това е да използвате скоби или скоби (въведено в MariaDB 10.4, вижте https://jira.mariadb.org/browse/MDEV-11953) и използването им за илюстриране какво се случва, изявлението ще изглежда така:
MariaDB> ИЗБЕРЕТЕ i.prod_id, p.prod_name -> ОТ инвентара i ПРИСЪЕДИНЯВАЙТЕ се към продуктите p ON i.prod_id =p.id -> UNION -> (ИЗБЕРЕТЕ oi.prod_id, p.prod_name -> ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> ОТ доставки d ПРИСЪЕДИНЕТЕ продукти p ON d.prod_id =p.id); +---------+-----------+ | prod_id | prod_name | +---------+-----------+ | 1 | Синя ваза | | 2 | Червена ваза | | 3 | Червен килим | +---------+-----------+ 3 реда в комплект (0,001 сек)
Сега нека използваме същата техника, за да наложим трите компонента на заявката да работят в строг ред:
MariaDB> (ИЗБЕРЕТЕ i.prod_id, p.prod_name -> ОТ инвентара i ПРИСЪЕДИНЕТЕ се към продуктите p ON i.prod_id =p.id -> UNION -> ИЗБЕРЕТЕ oi.prod_id, p.prod_name -> ОТ order_items oi ПРИСЪЕДИНЕТЕ се към продукти p ON oi.prod_id =p.id) -> INTERSECT -> SELECT d.prod_id, p.prod_name -> ОТ доставки d ПРИСЪЕДИНЕТЕ продукти p ON d.prod_id =p.id; +---------+-----------+ | prod_id | prod_name | +---------+-----------+ | 2 | Червена ваза | | 3 | Червен килим | +---------+-----------+ 2 реда в комплект (0,001 сек)
И накрая без скоби:
MariaDB [тест]> ИЗБЕРЕТЕ i.prod_id, p.prod_name -> ОТ инвентара i ПРИСЪЕДИНЯВАЙТЕ се към продукти p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> ОТ order_items oi ПРИСЪЕДИНЕТЕ продуктите p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> ОТ доставки d ПРИСЪЕДИНЕТЕ продукти p ON d.prod_id =p.id; +---------+-----------+ | prod_id | prod_name | +---------+-----------+ | 1 | Синя ваза | | 2 | Червена ваза | | 3 | Червен килим | +---------+-----------+ 3 реда в комплект (0,001 сек)
Виждаме, че MariaDB, следвайки стандарта, прие, че INTERSECT има предимство пред UNION . Което ни отвежда до Oracle. Нека опитаме горния SQL в Oracle с помощта на sqlplus:
SQL> ИЗБЕРЕТЕ i.prod_id, p.prod_name 2 ОТ инвентара i ПРИСЪЕДИНЕТЕ продукти p ON i.prod_id =p.id 3 UNION 4 SELECT oi.prod_id, p.prod_name 5 ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id 6 INTERSECT 7 SELECT d.prod_id, p.prod_name 8 ОТ доставки d ПРИСЪЕДИНЕТЕ продукти p ON d.prod_id =p.id; PROD_ID PROD_NAME ---------- ------------------------------ 2 ваза Червена 3 Червен килимпредварително>Какво става тук, питате? Е, Oracle не следва стандарта. Различните оператори на набора се третират като равни и никой няма предимство пред другия. Това е проблем, когато мигрирате приложения от Oracle към MariaDB и което е по-лошо е, че тази разлика е доста трудна за намиране. Не се създава грешка и се връщат данни и в много случаи се връщат правилните данни. Но в някои случаи, когато данните са като в нашия пример по-горе, се връщат грешни данни, което е проблем.
Ефект върху мигрирането на данни
И така, как да се справим с това, ако мигрираме приложение от Oracle към MariaDB? Има няколко опции:
- Пренапишете приложението, така че да приеме, че данните, върнати от заявка като тази, са в съответствие със стандарта на SQL и MariaDB.
- Пренапишете SQL изразите, като използвате скоби, така че MariaDB да връща същите данни като Oracle
- Или и това е най-умният начин, използвайте MariaDB
SQL_MODE=Oracleнастройка.
За последния и най-интелигентен начин да работим, трябва да работим с MariaDB 10.3.7 или по-нова версия (това беше предложено от вас наистина в https://jira.mariadb.org/browse/MDEV-13695). Нека проверим как работи това. Сравнете резултата от този SELECT с Oracle по-горе (който дава същия резултат) и този от MariaDB по-горе (който не):
MariaDB> задайте SQL_MODE=Oracle; Заявка ОК, 0 засегнати реда (0,001 сек) MariaDB> ИЗБЕРЕТЕ i.prod_id, p.prod_name -> ОТ инвентара i ПРИСЪЕДИНЕТЕ продукти p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> ОТ order_items oi ПРИСЪЕДИНЕТЕ продукти p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> ОТ доставки d ПРИСЪЕДИНЕТЕ продукти p ON d.prod_id =p.id; +---------+-----------+ | prod_id | prod_name | +---------+-----------+ | 2 | Червена ваза | | 3 | Червен килим | +---------+-----------+ 2 реда в комплект (0,002 сек)
Както можете да видите, когато SQL_MODE е настроен на Oracle , MariaDB наистина се държи като Oracle. Това не е единственото нещо, което SQL_MODE=Oracle разбира се, но това е една от по-малко известните области.
Заключение
Операторите за множество INTERSECT и EXCEPT не се използват толкова много, въпреки че се появяват тук-там и има някои приложения за тях. Примерите в този блог са повече, за да илюстрират как работят тези оператори, отколкото да покажат наистина добра употреба за тях. Сигурно има по-добри примери. Въпреки това, когато мигрирате от Oracle към MariaDB, операторите за набор от SQL са наистина полезни, тъй като много приложения на Oracle ги използват и както се вижда, MariaDB може да бъде подведена да работи точно като Oracle, който не е стандартен, но все пак служи за цел. Но по подразбиране, разбира се, MariaDB работи както трябва и следва SQL стандарта.
Приятно изпълнение на SQL
/Karlsson

