Въведение
Разделянето на свързани данни в отделни таблици може да бъде от полза от гледна точка на последователност, гъвкавост и определени видове производителност. Все пак имате нужда от разумен начин за повторно интегриране на записи, когато съответната информация обхваща множество таблици.
В релационни бази данни присъединява предлагат начин за комбиниране на записите в две или повече таблици въз основа на общи стойности на полета. Различните типове съединения могат да постигнат различни резултати в зависимост от това как трябва да се обработват несъответстващи редове. В това ръководство ще обсъдим различните типове обединения, които PostgreSQL предлага и как можете да ги използвате, за да комбинирате данни от таблици от множество източници.
Какво представляват присъединяванията?
Накратко, се присъединява са начин за показване на данни от множество таблици. Те правят това, като съединяват записи от различни източници въз основа на съвпадащи стойности в определени колони. Всеки получен ред се състои от запис от първата таблица, комбиниран с ред от втората таблица, въз основа на една или повече колони във всяка таблица с еднаква стойност.
Основният синтаксис на съединението изглежда така:
SELECT *FROM <first_table><join_type> <second_table> <join_condition>;
При обединяване всеки получен ред се конструира чрез включване на всички колони от първата таблица, последвани от всички колони от втората таблица. SELECT
част от заявката може да се използва за определяне на точните колони, които искате да се показват.
Множество реда могат да бъдат изградени от оригиналните таблици, ако стойностите в колоните, използвани за сравнение, не са уникални. Например, представете си, че имате колона, която се сравнява от първата таблица, която има два записа със стойност "червено". В съответствие с това е колона от втората таблица, която има три реда с тази стойност. Обединяването ще създаде шест различни реда за тази стойност, представляващи различните комбинации, които могат да бъдат постигнати.
Типът на присъединяването и условията за присъединяване определят как е конструиран всеки ред, който се показва. Това оказва влияние върху това, което се случва с редовете от всяка таблица, които правят и не имат съвпадение на условието за присъединяване.
За улеснение много присъединявания съвпадат с първичния ключ на една таблица със свързан външен ключ на втората таблица. Въпреки че първичните и външните ключове се използват само от системата на базата данни за поддържане на гаранции за последователност, връзката им често ги прави добър кандидат за условия за присъединяване.
Различни типове съединения
Предлагат се различни видове съединения, всяко от които потенциално ще доведе до различни резултати. Разбирането как е конструиран всеки тип ще ви помогне да определите кой е подходящ за различни сценарии.
Вътрешно присъединяване
Съединяването по подразбиране се нарича вътрешно присъединяване . В PostgreSQL това може да се посочи с помощта на INNER JOIN
или просто JOIN
.
Ето типичен пример, демонстриращ синтаксиса на вътрешно присъединяване:
SELECT *FROM table_1[INNER] JOIN table_2 ON table_1.id = table_2.table_1_id;
Вътрешното свързване е най-рестриктивният тип присъединяване, защото показва само редове, създадени чрез комбиниране на редове от всяка таблица. Всички редове в съставните таблици, които не са имали съответстващ аналог в другата таблица, се премахват от резултатите. Например, ако първата таблица има стойност „синя“ в колоната за сравнение, а втората таблица няма запис с тази стойност, този ред ще бъде потиснат от изхода.
Ако представите резултатите като диаграма на Вен на таблиците на компонентите, вътрешното свързване ви позволява да представите припокриващата се област на двата кръга. Нито една от стойностите, които са съществували само в една от таблиците, не се показва.
Ляво присъединяване
Лявото присъединяване е свързване, което показва всички записи, намерени във вътрешно съединение, плюс всички несъответстващи редове от първата таблица. В PostgreSQL това може да се посочи като LEFT OUTER JOIN
или просто като LEFT JOIN
.
Основният синтаксис на лявото присъединяване следва този модел:
SELECT *FROM table_1LEFT JOIN table_2 ON table_1.id = table_2.table_1_id;
Лявото свързване се конструира, като първо се извърши вътрешно свързване за конструиране на редове от всички съвпадащи записи в двете таблици. След това се включват и несравнимите записи от първата таблица. Тъй като всеки ред в съединението включва колоните и на двете таблици, колоните без съвпадения използват NULL
като стойност за всички колони във втората таблица.
Ако представите резултатите като диаграма на Вен на таблиците на компонентите, лявото присъединяване ви позволява да представите целия ляв кръг. Частите от левия кръг, представени от пресечната точка между двата кръга, ще имат допълнителни данни, допълнени от дясната таблица.
Дясно присъединяване
Дясното присъединяване е свързване, което показва всички записи, открити във вътрешното съединение, плюс всички несъответстващи редове от втората таблица. В PostgreSQL това може да се посочи като RIGHT OUTER JOIN
или просто като RIGHT JOIN
.
Основният синтаксис на дясното присъединяване следва този модел:
SELECT *FROM table_1RIGHT JOIN table_2 ON table_1.id = table_2.table_1_id;
Дясното свързване се конструира, като първо се извърши вътрешно свързване за конструиране на редове от всички съвпадащи записи в двете таблици. След това се включват и несравнимите записи от втората таблица. Тъй като всеки ред в съединението включва колоните и на двете таблици, колоните без съвпадения използват NULL
като стойност за всички колони в първата таблица.
Ако представите резултатите като диаграма на Вен на таблиците на компонентите, дясното присъединяване ви позволява да представите целия десен кръг. Частите от десния кръг, представени от пресечната точка между двата кръга, ще имат допълнителни данни, допълнени от лявата таблица.
Пълно присъединяване
Пълното присъединяване е присъединяване, което показва всички записи, открити във вътрешното присъединяване, плюс всички несъответстващи редове от двете таблици на компонентите. В PostgreSQL това може да бъде посочено като FULL OUTER JOIN
или просто като FULL JOIN
.
Основният синтаксис на пълното присъединяване следва този модел:
SELECT *FROM table_1FULL JOIN table_2 ON table_1.id = table_2.table_1_id;
Пълно обединяване се конструира, като първо се извърши вътрешно свързване за конструиране на редове от всички съвпадащи записи в двете таблици. След това се включват и несравнимите записи от двете таблици. Тъй като всеки ред в съединението включва колоните и на двете таблици, колоните без съвпадения използват NULL
като стойност за всички колони в несъответстващата друга таблица.
Ако представите резултатите като диаграма на Вен на таблиците на компонентите, пълното свързване ви позволява да представите изцяло и двата кръга на компонентите. Пресечната точка на двете окръжности ще има стойности, предоставени от всяка от таблиците на компонентите. Частите от кръговете извън зоната на припокриване ще имат стойностите от таблицата, към която принадлежат, като се използва NULL
за да попълните колоните, намерени в другата таблица.
Кръстосано присъединяване
Специално присъединяване, наречено CROSS JOIN
също е наличен. Кръстосаното свързване не използва никакви сравнения, за да определи дали редовете във всяка таблица съвпадат един с друг. Вместо това резултатите се конструират чрез просто добавяне на всеки от редовете от първата таблица към всеки от редовете на втората таблица.
Това произвежда декартово произведение на редовете в две или повече таблици. Всъщност този стил на присъединяване комбинира редове от всяка таблица безусловно. Така че, ако всяка таблица има три реда, получената таблица ще има девет реда, съдържащи всички колони от двете таблици.
Например, ако имате таблица, наречена t1
комбиниран с таблица, наречена t2
, всеки с редове r1
, r2
и r3
, резултатът ще бъде девет реда, комбинирани така:
t1.r1 + t2.r1t1.r1 + t2.r2t1.r1 + t2.r3t1.r2 + t2.r1t1.r2 + t2.r2t1.r2 + t2.r3t1.r3 + t2.r1t1.r3 + t2.r2t1.r3 + t2.r3
Самоприсъединяване
Самообединяване е всяко присъединяване, което комбинира редовете на таблица със себе си. Може да не е веднага ясно как това може да бъде полезно, но всъщност има много често срещани приложения.
Често таблиците описват обекти, които могат да изпълняват множество роли във връзка една с друга. Например, ако имате таблица с people
, всеки ред може потенциално да съдържа mother
колона, която препраща към други people
на масата. Самообединяването би ви позволило да съедините тези различни редове, като присъедините втори екземпляр на таблицата към първия, където тези стойности съвпадат.
Тъй като самообединяването препраща към една и съща таблица два пъти, се изискват псевдоними на таблицата, за да се разграничат препратките. В примера по-горе, например, можете да се присъедините към двата екземпляра на people
таблица, използваща псевдонимите people AS children
и people AS mothers
. По този начин можете да посочите кой екземпляр на таблицата имате предвид, когато дефинирате условия за присъединяване.
Ето още един пример, този път представящ взаимоотношенията между служители и мениджъри:
SELECT *FROM people AS employeeJOIN people AS manager ON employee.manager_id = manager.id;
Условия за присъединяване
Когато комбинирате таблици, условието за присъединяване определя как редовете ще бъдат съпоставени заедно, за да образуват съставните резултати. Основната предпоставка е да се дефинират колоните във всяка таблица, които трябва да съвпадат, за да се случи свързването на този ред.
ON
клауза
Най-стандартният начин за дефиниране на условията за присъединяване на таблици е с ON
клауза. ON
клаузата използва знак за равенство, за да посочи точните колони от всяка таблица, които ще бъдат сравнени, за да се определи кога може да се случи присъединяване. PostgreSQL използва предоставените колони, за да съединява редовете от всяка таблица.
ON
Клаузата е най-многословната, но и най-гъвкавата от наличните условия за присъединяване. Той позволява специфичност, независимо от това колко стандартизирани са имената на колоните на всяка комбинирана таблица.
Основният синтаксис на ON
клаузата изглежда така:
SELECT *FROM table1JOIN table2ON table1.id = table2.ident;
Тук редовете от table1
и table2
ще се присъединява всеки път, когато id
колона от table1
съвпада с ident
колона от table2
. Тъй като се използва вътрешно свързване, резултатите ще покажат само редовете, които са били присъединени. Тъй като заявката използва заместващия знак *
символ, ще се покажат всички колони от двете таблици.
Това означава, че и двата id
колона от table1
и ident
колона от table2
ще бъдат показани, въпреки че имат същата точна стойност по силата на удовлетворяването на условието за присъединяване. Можете да избегнете това дублиране, като извикате точните колони, които искате да покажете в SELECT
списък с колони.
USING
клауза
USING
Клаузата е съкратено за определяне на условията на ON
клауза, която може да се използва, когато колоните, които се сравняват, имат едно и също име в двете таблици. USING
клаузата взема списък, затворен в скоби, на споделените имена на колони, които трябва да се сравняват.
Общият синтаксис на USING
клаузата използва този формат:
SELECT *FROM table1JOIN table2USING (id, state);
Това обединение комбинира table1
с table2
когато две колони, които и двете таблици споделят (id
и state
) всеки има съвпадащи стойности.
Същото това присъединяване може да бъде изразено по-подробно с помощта на ON
така:
SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state;
Въпреки че и двете горни съединения биха довели до конструиране на едни и същи редове с едни и същи данни, те ще бъдат показани малко по-различно. Докато ON
клаузата включва всички колони от двете таблици, USING
клаузата потиска дублиращите се колони. Така че вместо да има два отделни id
колони и две отделни state
колони (по една за всяка таблица), резултатите ще имат само една от всяка от споделените колони, последвана от всички други колони, предоставени от table1
и table2
.
NATURAL
клауза
NATURAL
Клаузата е още една стенография, която може допълнително да намали многословността на USING
клауза. NATURAL
присъединяването не посочва никое колони, които да бъдат съпоставени. Вместо това PostgreSQL автоматично ще се присъедини към таблиците въз основа на всички колони, които имат съвпадащи колони във всяка база данни.
Общият синтаксис на NATURAL
клаузата за присъединяване изглежда така:
SELECT *FROM table1NATURAL JOIN table2;
Ако приемем, че table1
и table2
и двете имат колони с име id
, state
и company
, горната заявка ще бъде еквивалентна на тази заявка с помощта на ON
клауза:
SELECT *FROM table1JOIN table2ON table1.id = table2.id AND table1.state = table2.state AND table1.company = table2.company;
И тази заявка използва USING
клауза:
SELECT *FROM table1JOIN table2USING (id, state, company);
Като USING
клауза, NATURAL
Клаузата потиска дублиращите се колони, така че в резултатите ще има само един екземпляр на всяка от присъединените колони.
Докато NATURAL
клаузата може да намали многословността на вашите заявки, трябва да внимавате, когато я използвате. Тъй като колоните, използвани за свързване на таблиците, се изчисляват автоматично, ако колоните в таблиците на компонентите се променят, резултатите могат да бъдат значително различни поради новите условия за присъединяване.
Условията за присъединяване и WHERE
клауза
Условията за присъединяване споделят много характеристики със сравненията, използвани за филтриране на редове с данни с помощта на WHERE
клаузи. И двете конструкции дефинират изрази, които трябва да се оценят като истина, за да се вземе предвид редът. Поради това не винаги е интуитивно каква е разликата между включването на допълнителни сравнения в WHERE
конструкция спрямо дефинирането им в самата клауза за свързване.
За да разберем разликите, които ще доведат до това, трябва да разгледаме реда, в който PostgreSQL обработва различни части от заявка. В този случай предикатите в условието за присъединяване се обработват първо за конструиране на виртуалната обединена таблица в паметта. След този етап изразите в WHERE
клауза се оценяват за филтриране на получените редове.
Като пример, да предположим, че имаме две таблици, наречени customer
и order
че трябва да се обединим. Искаме да обединим двете таблици, като съпоставим customer.id
колона с order.customer_id
колона. Освен това се интересуваме от редовете в order
таблица, която има product_id
от 12345.
Предвид горните изисквания имаме две условия, които ни интересуват. Начинът, по който изразяваме тези условия обаче, ще определи резултатите, които ще получим.
Първо, нека използваме и двете като условия за присъединяване за LEFT JOIN
:
SELECT customer.id AS customer_id, customer.name, order.id AS order_id, order.product_idFROM customerLEFT JOIN orderON customer.id = order.customer_id AND order.product_id = 12345;
Резултатите биха могли да изглеждат по следния начин:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345 4380 | Acme Co | | 320 | Other Co | | 20 | Early Co | | 8033 | Big Co | |(7 rows)
PostgreSQL стигна до този резултат, като извърши следните операции:
- Комбинирайте всички редове в
customer
таблица сorder
таблица, където:customer.id
съвпада сorder.customer_id
.order.product_id
съвпада с 12345
- Тъй като използваме ляво присъединяване, включете всяко несъответстващо редове от лявата таблица (
customer
), изпълване на колоните от дясната таблица (order
) сNULL
стойности. - Показване само на колоните, изброени в
SELECT
спецификация на колона.
Резултатът е, че всички наши съединени редове отговарят и на двете условия, които търсим. Въпреки това, лявото присъединяване кара PostgreSQL да включва и всички редове от първата таблица, които не отговарят на условието за присъединяване. Това води до „оставени“ редове, които изглежда не следват очевидното намерение на заявката.
Ако преместим втората заявка (order.product_id
=12345) към WHERE
клауза, вместо да я включваме като условие за присъединяване, получаваме различни резултати:
SELECT customer.id AS customer_id, customer.name, order.id AS order_id, order.product_idFROM customerLEFT JOIN orderON customer.id = order.customer_idWHERE order.product_id = 12345;
Този път се показват само три реда:
customer_id | name | order_id | product_id ------------+----------+----------+------------ 4380 | Acme Co | 480 | 12345 4380 | Acme Co | 182 | 12345 320 | Other Co | 680 | 12345(3 rows)
Редът, в който се извършват сравненията, е причината за тези разлики. Този път PostgreSQL обработва заявката по следния начин:
- Комбинирайте всички редове в
customer
таблица сorder
таблица, къдетоcustomer.id
съвпада сorder.customer_id
. - Тъй като използваме ляво присъединяване, включете всяко несъответстващо редове от лявата таблица (
customer
), изпълване на колоните от дясната таблица (order
) сNULL
стойности. - Оценете
WHERE
клауза за премахване на всички редове, които нямат 12345 като стойност заorder.product_id
колона. - Показване само на колоните, изброени в
SELECT
спецификация на колона.
Този път, въпреки че използваме ляво присъединяване, WHERE
клаузата съкращава резултатите, като филтрира всички редове без правилния product_id
. Тъй като всички несъответстващи редове ще имат product_id
зададен на NULL
, това премахва всички несъответстващи редове, които са били попълнени от лявото присъединяване. Той също така премахва всеки от редовете, на които съответства условието за присъединяване, които не са преминали този втори кръг от проверки.
Разбирането на основния процес, който PostgreSQL използва за изпълнение на вашите заявки, може да ви помогне да избегнете някои лесни за правене, но трудни за отстраняване грешки, докато работите с вашите данни.
Заключение
В това ръководство разгледахме как присъединяванията позволяват на релационни бази данни да комбинират данни от различни таблици, за да предоставят по-ценни отговори. Говорихме за различните присъединявания, които PostgreSQL поддържа, начина, по който всеки тип събира своите резултати, и какво да очаквате, когато използвате специфични видове съединения. След това разгледахме различни начини за дефиниране на условията на присъединяване и разгледахме как е взаимодействието между присъединяванията и WHERE
клаузата може да доведе до изненади.
Съединенията са съществена част от това, което прави релационните бази данни достатъчно мощни и гъвкави, за да обработват толкова много различни видове заявки. Организирането на данни с помощта на логически граници, като същевременно можете да рекомбинирате данните по нови начини за всеки отделен случай, дава на релационни бази данни като PostgreSQL невероятна гъвкавост. Научаването как да извършвате това свързване между таблици ще ви позволи да създавате по-сложни заявки и да разчитате на базата данни, за да създадете пълни снимки на вашите данни.