Еднотаблични и многотаблични заявки SELECT. JOIN.

Еднотабличен SELECT:

Еднотабличните SELECT заявки имат за цел да извлекат информация от една таблица по определени критерии. Например търсим кои са всички ученици. Или пък всички ученици на които адресът им е „Младост“ и тн.

Синтаксис на еднотабличен SELECT:
След SELECT изброяваме колоните, разделени със запетая или символа ” “, ако искаме всички колони. После уточняваме във FROM от коя таблица са тези колони и накрая ако има някакви критерии ги задаваме в where клаузата(ограничаващо условие).

SELECT <колона1>,<колона2>,<колонаN>
FROM <таблица>
WHERE <условие>;     ->ограничаващо условие

Например – нека да изведем информацията за всички ученици от нашата по-стара база school_sport_clubs:

SELECT id, name,egn,address,phone,class
FROM students;

В случая нямаме ограничаващи условия, затова пропускаме where клаузата.
Нека да изведем всички ученици, на които id-тата са между 2 и 5.

SELECT *
FROM students
WHERE id >= 2 AND id <= 5

Както виждате логическите оператори И и ИЛИ важат – AND и OR.

5

Аналогично можем да използваме и оператор BETWEEN:

SELECT *
FROM students
WHERE id BETWEEN 2 AND 5

И резултатът е същият.
Да изведем информацията за всички студенти, на които имената им започват с буквата „I“:

SELECT *
FROM school_sport_clubs.students
WHERE name LIKE 'I%';

Такова търсене може да се реализира чрез маска за търсене – чрез оператор LIKE. В кавичките след него се задава т.нар. маска. Например:

  • започва с И -> ‘И%‘
  • завършва на И -> ‘%И‘
  • съдържа И -> ‘%И%‘

Ето и резултата:

6

Многотаблични заявки SELECT:

До тук добре, но какво ще се случи ако искаме да комбинираме резултати от повече от една таблица? Това е често срещана задача в релационните бази данни, особено в по- нормализираните – дори по-често срещана от еднотабличния селект. Трябва обаче по някакъв начин да осигурим нормално представяне на данните- например искаме да изведем всички треньори заедно със спортовете, на които те тренират учениците от нашата база. Да, но в таблицата с треньори няма информация за това кой треньор по какво е. В таблицата със спортове също няма информация кой е треньор по дадения спорт. За да разрешим този проблем съществуват няколко класически решения – JOIN и вложен селект.

Съединения на таблици – JOIN заявки:

JOIN заявките се разделят най- общо на вътрешни и външни съединения. За да може правилно да свържем двете таблици, ще ни трябва някакъв критерий, на базата на който да съединим ред от едната с ред от другата.   Ще го наричаме свързващо условие.

INNER JOIN

Такова съединение между две релации, при което се получава резултатна таблица, съставена от колони от двете, базирана на свързващо условие. Заявката сравнява всеки ред от първата с всеки ред от втората за да открие всички двойки, които отговарят на свързващото условие. Получава се нещо подобно:

7

Ако свързващото условие не е изпълнено, то реда не се добавя в резултатното съединение. Нека да реализираме следния пример: искаме всички групи, заедно с името на спорта им. Да, но в таблицата с sportGroups има единствено id към таблицата sports- ще трябва да ги свържем именно на базата на този критерий:

SELECT sportgroups.location,
sportgroups.dayOfWeek,
sportgroups.hourOfTraining,
sportgroups.dayOfWeek,
sports.name
FROM sportgroups JOIN sports
ON sportgroups.sport_id = sports.id;

8

Най – общо казано се спазва следният синтаксис:

SELECT <табл1.колона1>,<табл2.колона2>
FROM <табл1> JOIN <табл2>
ON <свързващо условие> 
WHERE <ограничаващо условие>;

Условието в ON ще наричаме свързващо. То трябва да е изпълнено и за двете таблици.
Нека да реализираме примера с извеждане на всички треньори заедно със спортовете, на които те тренират учениците от нашата база. Двете таблици треньори и спортове обаче не са свързани директно, а са свързани посредством трета таблица – sportGroups – в нея присъства id на спорт и id на треньор. А защо не сложихме директно id на треньор в таблицата sports? Отговорът е защото един треньор може да преподава повече от един спорт.
Нека преди това да добавим още една спортна група, която да я тренира треньор с id 2 и тя да бъде по волейбол(защото в момента в базата ни има само 2 групи по футбол).

INSERT  INTO sportgroups(location,dayOfWeek,hourOfTraining,sport_id,coach_id)
VALUES('Sofia- Liylin 7','Sunday','09:00:00',2,2);

Нека да напишем заявката:

SELECT coaches.name,sports.name
from coaches JOIN sports
ON coaches.id IN(
SELECT coach_id
FROM sportgroups
WHERE sportgroups.sport_id = sports.id
);

9

Използвахме оператор IN () – аналогично на =, но за множество от стойности. Може да се преведе като „принадлежи на“. Всъщност търсим такива треньори, на които id-то им „принадлежи на“ множеството от всички coach_id от таблица sportgroups, които… За да вземем всички coach_id ползваме още един SELECT – нещо ще наричаме вложен селект.

Вложен SELECT:

При таблици между които няма пряка връзка се налага да се „минава“ през една или друга таблица за да се стигне до трета.  Типичен пример е горния.

Нека да ограничим нашето търсене само за треньор с id 1- тоест искаме да разберем по какво е треньор той.

SELECT coaches.name,sports.name
from coaches JOIN sports
ON coaches.id IN(
SELECT coach_id
FROM sportgroups
WHERE sportgroups.sport_id = sports.id
)
where coaches.id = 1;

Имаме две условия:

  • ON – свързващо
  • WHERE – ограничаващо
  • Не сме ограничени да ги разменим или да ги обединим с логическо AND по между им и заявката ще работи по същия начин. Реално между двете условия е приложено логическо И.
  • Добра практика е в ON да слагаме само свързващи условия, а всички останали – в ограничаващи.

Как да избегнем вложения селект? Чрез JOIN  на трите таблици:

SELECT DISTINCT coaches.name,sports.name
from coaches JOIN sportgroups
ON coaches.id = sportgroups.coach_id
JOIN sports
ON sportgroups.sport_id = sports.id;

Резултатът ще бъде същият. Всъщност сме добавили DISTINCT – което означава „без повторение“, тъй като в резултат на JOIN ще се проверява всеки ред с всеки ред, където е изпълнено условието в ON, и ще ни повтори треньорите, в случай че ги има повече от 1 път.

АНАЛИЗ:

  • Към таблицата coaches се добавя таблица sportGroups на базата на ON условието между тях.
  • Към резултатната таблица се добавя и таблицата sports отново на ON условието.
  • В крайния резултат обаче нито една колона от sportGroups не участва – ние я използваме просто защото тя свързва другите две таблици.
    • + заявката е по-лесно четима.
    • заявката е по-бавна. Причината е, че при вложен селект съединението се прави с подмножество на таблицата, а не на целите таблици. Затова, когато е възможно се предпочита пред чистия

Практическо правило при съединения на таблици:

Правете JOIN само между таблици, които са споменати в SELECT и колони от тях ще присъстват в крайния резултат.

Нищо не пречи да ограничим горния резултат за треньор с id 1.

SELECT DISTINCT coaches.name,sports.name
from coaches JOIN sportgroups
ON coaches.id = sportgroups.coach_id
JOIN sports
ON sportgroups.sport_id = sports.id
WHERE coaches.id = 1;

Какво ще се случи, обаче, ако в едната от таблиците стойността на колоната, по която се сравнява в свързващото условие е NULL? Сравненията с NULL винаги дават лъжа, така че такива редове няма да бъдат добавяни в резултата. Нека да дадем пример. Да създадем една спортна група по футбол, но за сега не знаем кой ще я тренира, затова оставяме полето coach_id NULL.

INSERT INTO school_sport_clubs.sportgroups 
(location, dayOfWeek, hourOfTraining, sport_id,coach_id)
 VALUES ('Sofia-Nadezhda', 'Sunday', '08:00', 1,NULL);

Таблицата изглежда така:

10
Сега ще се изведем всички спортни групи, заедно с треньорите им – тоест ще съединим двете таблици sportGroups и coaches  на базата на външния ключ от едната към другата:

SELECT sportgroups.location,
sportgroups.dayOfWeek,
sportgroups.hourOfTraining,
sportgroups.sport_id,
coaches.name
FROM sportgroups JOIN coaches
ON sportgroups.coach_id = coaches.id;

Резултатът е:

11

Естествено свързващото условие дава лъжа за ред номер 4 от таблицата sportGroups и съответно треньор не беше добавен. Как да направим така, че независимо, че треньор не е въведен, записът за спортната група да излезе, но с треньор NULL>? Отговорът е чрез външно съединение.

Външни съединения – OUTER JOIN:

Външните съединения се делят на две – LEFT и RIGHT OUTER JOIN. Най-общо казано, те ни дават всички редове от лявата/дясната таблица, без значение от това дали има съответстващи им редове от дясната/лявата таблица.

12

Може да се каже, че при ляво съединение „натежава“ лявата таблица, а при дясно – дясната. Думата  OUTER  не е задължителна, така, както и INNER. Нека да видим как ще се отрази на резултата добавянето на LEFT :

SELECT sportgroups.location,
sportgroups.dayOfWeek,
sportgroups.hourOfTraining,
sportgroups.sport_id,
coaches.name
FROM sportgroups LEFT OUTER JOIN coaches
ON sportgroups.coach_id = coaches.id;

13

Какво ще се случи ако използваме RIGHT JOIN – ще се изведат всички треньори и ако са записани в sportGroups да тренират някоя група- ще се появи и групата, ако не – данните за групата ще бъдат празни:

SELECT sportgroups.location,
sportgroups.dayOfWeek,
sportgroups.hourOfTraining,
sportgroups.sport_id,
coaches.name
FROM sportgroups RIGHT JOIN coaches
ON sportgroups.coach_id = coaches.id;

Ето и резултата:

14

Задача:

Изведете списък с имената на учениците и класовете им, както и номера (id) на групата в която тренират за всички ученици, които тренират в понеделник от 08:00 и са при треньор с име „Иван Тодоров Петров“, но само за групите му по футбол.

use school_sport_clubs;


SELECT students.name, students.class, sportgroups.id
 FROM students JOIN sportgroups
 ON students.id IN (
	SELECT student_id
	FROM student_sport
	WHERE student_sport.sportGroup_id = sportgroups.id
 )
 WHERE sportgroups.id IN(
	SELECT sportgroup_id
    FROM student_sport
    WHERE sportGroup_id IN(
		SELECT id
		FROM sportgroups
		WHERE dayOfWeek = 'Monday'
		AND hourOfTraining = '08:00:00'
		AND coach_id IN(
			SELECT id
			FROM coaches
			WHERE name = 'Иван Тодоров Петров'
		)
        AND sport_id =(
			SELECT id
			FROM sports
            WHERE name = 'Football'
		)
    )
 );

 

15

DBScript

Даниел Джолев

 

6 thoughts on “Еднотаблични и многотаблични заявки SELECT. JOIN.”

  1. Може ли да ни дадете базата данни попълнена с някакви данни,за да може да си тестваме заявките.

    1. Всички insert заявки ги има тук и в предните статии. Просто ги изпълнете.

  2. Задачи за упражнение 2 по бази данни:

    1. Въведете още 2 групи от по 5 ученика в базата ви данни- всяка в различен ден и по различен спорт.
    2. Напишете UPDATE заявка, с която премествате всички ученици на треньор „Иван Тодоров Петров“, при треньор на име Илиян Тодоров Георгиев, тъй като първият напуска. НЕ търсете предарително ид-тата на треньорите. Трябва да ги откриете в заявката ви по име(сигурни сме че имената им са уникални в нашата база.). Трябва да напишете точно ЕДНА заявка, с която да преместите всички ученици при новия треньор, като не знаете кои са id-тата на учениците.
    3. Изведете име на треньор, спорт, място на провеждане на тренировките, за всички ученици, които живеят в София, и са набор 94, а класът им е по-голям от 9ти. Тренировките са само сутрешни( тоест преди 12:00).

  3. Здравей не разбирам значението на един фрагмент в последния пример от темата. Защо е необходим този фрагмент от кода?:

    WHERE sportgroups.id IN(
    SELECT sportgroup_id
    FROM student_sport
    WHERE sportGroup_id IN(
    SELECT id
    FROM sportgroups

    като вече сме достигнали до sportgroups в диаграмата на базата и с този код пак се качваме в student_sport от където идваме към sportgroups? Не може ли просто да си сложим един ограничителен where и да си ги навържем с and нещата нещо от рода на:

    SELECT students.name, students.class, sportgroups.id
    FROM students JOIN sportgroups
    ON students.id IN (
    SELECT student_id
    FROM student_sport
    WHERE student_sport.sportGroup_id = sportgroups.id
    )

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

    where sportgroups.dayOfWeek = ‘….’
    and sportgroups.hourOfTraining = ‘….’
    and sport_id = ( select
    sports.id from sports
    where sports.name = ‘….’)
    and coach_id = (select coaches.id from coaches
    where coaches.name = ‘….’);

    1. Здравейте,
      Да, може. Има различни подходи за решаване на една и съшата задача. В различните случаи на обеми и нормализация на базата, различните решения имт плюсове и муниси по отношение на производителността.

Leave a Reply

Your email address will not be published. Required fields are marked *