Еднотабличен 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.
Аналогично можем да използваме и оператор BETWEEN:
SELECT * FROM students WHERE id BETWEEN 2 AND 5
И резултатът е същият.
Да изведем информацията за всички студенти, на които имената им започват с буквата „I“:
SELECT * FROM school_sport_clubs.students WHERE name LIKE 'I%';
Такова търсене може да се реализира чрез маска за търсене – чрез оператор LIKE. В кавичките след него се задава т.нар. маска. Например:
- започва с И -> ‘И%‘
- завършва на И -> ‘%И‘
- съдържа И -> ‘%И%‘
Ето и резултата:
Многотаблични заявки SELECT:
До тук добре, но какво ще се случи ако искаме да комбинираме резултати от повече от една таблица? Това е често срещана задача в релационните бази данни, особено в по- нормализираните – дори по-често срещана от еднотабличния селект. Трябва обаче по някакъв начин да осигурим нормално представяне на данните- например искаме да изведем всички треньори заедно със спортовете, на които те тренират учениците от нашата база. Да, но в таблицата с треньори няма информация за това кой треньор по какво е. В таблицата със спортове също няма информация кой е треньор по дадения спорт. За да разрешим този проблем съществуват няколко класически решения – JOIN и вложен селект.
Съединения на таблици – JOIN заявки:
JOIN заявките се разделят най- общо на вътрешни и външни съединения. За да може правилно да свържем двете таблици, ще ни трябва някакъв критерий, на базата на който да съединим ред от едната с ред от другата. Ще го наричаме свързващо условие.
INNER JOIN
Такова съединение между две релации, при което се получава резултатна таблица, съставена от колони от двете, базирана на свързващо условие. Заявката сравнява всеки ред от първата с всеки ред от втората за да открие всички двойки, които отговарят на свързващото условие. Получава се нещо подобно:
Ако свързващото условие не е изпълнено, то реда не се добавя в резултатното съединение. Нека да реализираме следния пример: искаме всички групи, заедно с името на спорта им. Да, но в таблицата с sportGroups има единствено id към таблицата sports- ще трябва да ги свържем именно на базата на този критерий:
SELECT sportgroups.location, sportgroups.dayOfWeek, sportgroups.hourOfTraining, sportgroups.dayOfWeek, sports.name FROM sportgroups JOIN sports ON sportgroups.sport_id = sports.id;
Най – общо казано се спазва следният синтаксис:
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 );
Използвахме оператор 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);
Таблицата изглежда така:
Сега ще се изведем всички спортни групи, заедно с треньорите им – тоест ще съединим двете таблици sportGroups и coaches на базата на външния ключ от едната към другата:
SELECT sportgroups.location, sportgroups.dayOfWeek, sportgroups.hourOfTraining, sportgroups.sport_id, coaches.name FROM sportgroups JOIN coaches ON sportgroups.coach_id = coaches.id;
Резултатът е:
Естествено свързващото условие дава лъжа за ред номер 4 от таблицата sportGroups и съответно треньор не беше добавен. Как да направим така, че независимо, че треньор не е въведен, записът за спортната група да излезе, но с треньор NULL>? Отговорът е чрез външно съединение.
Външни съединения – OUTER JOIN:
Външните съединения се делят на две – LEFT и RIGHT OUTER JOIN. Най-общо казано, те ни дават всички редове от лявата/дясната таблица, без значение от това дали има съответстващи им редове от дясната/лявата таблица.
Може да се каже, че при ляво съединение „натежава“ лявата таблица, а при дясно – дясната. Думата 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;
Какво ще се случи ако използваме 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;
Ето и резултата:
Задача:
Изведете списък с имената на учениците и класовете им, както и номера (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' ) ) );
Даниел Джолев
Може ли да ни дадете базата данни попълнена с някакви данни,за да може да си тестваме заявките.
Всички insert заявки ги има тук и в предните статии. Просто ги изпълнете.
Задачи за упражнение 2 по бази данни:
1. Въведете още 2 групи от по 5 ученика в базата ви данни- всяка в различен ден и по различен спорт.
2. Напишете UPDATE заявка, с която премествате всички ученици на треньор „Иван Тодоров Петров“, при треньор на име Илиян Тодоров Георгиев, тъй като първият напуска. НЕ търсете предарително ид-тата на треньорите. Трябва да ги откриете в заявката ви по име(сигурни сме че имената им са уникални в нашата база.). Трябва да напишете точно ЕДНА заявка, с която да преместите всички ученици при новия треньор, като не знаете кои са id-тата на учениците.
3. Изведете име на треньор, спорт, място на провеждане на тренировките, за всички ученици, които живеят в София, и са набор 94, а класът им е по-голям от 9ти. Тренировките са само сутрешни( тоест преди 12:00).
Здравей не разбирам значението на един фрагмент в последния пример от темата. Защо е необходим този фрагмент от кода?:
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 = ‘….’);
Здравейте,
Да, може. Има различни подходи за решаване на една и съшата задача. В различните случаи на обеми и нормализация на базата, различните решения имт плюсове и муниси по отношение на производителността.
Мисля че частта :” SELECT sportgroup_id
FROM student_sport
WHERE sportGroup_id IN”
е излишна.