FROM — ключевое слово, подобно SELECT, которое должно быть представ-
лено в каждом запросе. Оно сопровождается пробелом и затем именами таблиц, используемых в качестве источника информации. В случае если указано более одного имени таблицы, неявно подразумевается, что над перечисленными таблицами осуществляется операция декартова произведения. Таблицам можно присвоить имена-псевдонимы, что бывает полезно для осуществления операции соединения таблицы с самой собою или для доступа из вложенного подзапроса к текущей записи внешнего запроса (вложенные подзапросы здесь не рассматриваются).
Все последующие разделы оператора SELECT являются необязательными.
Самый простой запрос SELECT без необязательных частей соответствует просто декартову произведению. Например, выражение
SELECT * FROM R1, R2
соответствует декартову произведению таблиц R1 и R2. Выражение
SELECT R1.A, R2.B FROM R1, R2
соответствует проекции декартова произведения двух таблиц на два столбца A из таблицы R1 и B из таблицы R2, при этом дубликаты всех строк сохранены, в отличие от операции проектирования в реляционной алгебре, где при проектировании по умолчанию все дубликаты кортежей уничтожаются.
WHERE — ключевое слово, за которым следует предикат — условие, налагаемое -на запись в таблице, которому она должна удовлетворять, чтобы попасть в выборку, аналогично операции селекции в реляционной алгебре.
Рассмотрим базу данных, которая моделирует сдачу сессии в некотором учебном заведении. Пусть она состоит из трех отношений R1, R2, R3. Будем считать, что они представлены таблицами R1, R2 и R3 соответственно.
R1 = (ФИО, Дисциплина, |
Оценка) ; R2 = (ФИО, Группа) ; R3 = |
||||
(Группы, Дисциплина) |
|||||
R1 |
|||||
ФИО |
Дисциплина |
Оценка |
|||
Петров Ф. И. |
Базы данных |
5 |
|||
Сидоров К. А. |
Базы данных |
4 |
|||
Миронов А. В. |
Базы данных |
2 |
|||
Степанова К. Е. |
Базы данных |
2 |
|||
Крылова Т. С. |
Базы данных |
5 |
|||
Сидоров К. А. |
Теория информации |
4 |
|||
Степанова К. Е. |
Теория информации |
2 |
|||
Крылова Т. С. |
Теория информации |
5 |
|||
Миронов А. В. |
Теория информации |
Null |
|||
Владимиров В. А. |
Базы данных |
5 |
|||
Трофимов П. А. |
Сети и телекоммуникации |
4 |
|||
Иванова Е. А. |
Сети и телекоммуникации |
5 |
|||
Уткина Н. В. |
Сети и телекоммуникации |
5 |
Владимиров В. А. Английский язык
Трофимов П. А. |
Английский язык |
|||
Иванова Е. А. |
Английский язык |
|||
Петров Ф. И. |
Английский язык |
|||
R2
ФИО
Петров Ф. И.
Сидоров К. А.
Миронов А. В.
Крылова Т. С.
Владимиров В. А.
Степанова К. Е.
Трофимов П. А.
Иванова Е. А.
Уткина Н. В.
Группа
4906
4906
4906
4906
4906
4906
4807
4807
4807
R3 |
|||
Группа |
Дисциплина |
||
4906 |
Базы данных |
||
4906 |
Теория информации |
||
4906 |
Английский язык |
||
4807 |
Английский язык |
||
4807 |
Сети и телекоммуникации |
Приведем несколько примеров использования оператора SELECT.
Вывести список всех групп (без повторений), где должны пройти экзамены.
SELECT DISTINCT Группы FROM R3
Результат:
Вывести список студентов, которые сдали экзамен по дисциплине «Базы данных» на «отлично».
SELECT ФИО
FROM R1
WHERE Дисциплина = «Базы данных» AND Оценка = 5
Результат:
ФИО
Петров Ф. И.
Крылова Т. С.
Владимирова В. А.
Вывести список всех студентов, которым надо сдавать экзамены с указанием названий дисциплин, по которым должны проводиться эти экзамены.
SELECT ФИО,Дисциплина
FROM R2,R3
WHERE R2.Группа = R3.Группа;
Здесь часть WHERE задает условия соединения отношений R2 и R3, при отсутствии условий соединения в части WHERE результат будет эквивалентен расширенному декартову произведению, и в этом случае каждому студенту были бы приписаны все дисциплины из отношения R3, а не те, которые должна сдавать его группа.
Результат:
ФИО |
Дисциплина |
|||
Петров Ф. И. |
Базы данных |
|||
Сидоров К. А. |
Базы данных |
|||
Миронов А. В. |
Базы данных |
|||
Степанова К. Е. |
Базы данных |
|||
Крылова Т. С. |
Базы данных |
|||
Владимиров В. А. |
Базы данных |
|||
Петров Ф. И. |
Теория информации |
|||
Сидоров К. А. |
Теория информации |
|||
Миронов А. В. |
Теория информации |
|||
Степанова К. Е. |
Теория информации |
|||
Крылова Т. С. |
Теория информации |
|||
Владимиров В. А. |
Теория информации |
|||
Петров Ф. И. |
Английский язык |
|||
Сидоров К. А. |
Английский язык |
|||
Миронов А. В. |
Английский язык |
|||
Степанова К. Е. |
Английский язык |
|||
Крылова Т. С. |
Английский язык |
|||
Владимиров В. А. |
Английский язык |
|||
Трофимов П. А. |
Сети и телекоммуникации |
|||
Иванова Е. А. |
Сети и телекоммуникации |
|||
Уткина Н. В. |
Сети и телекоммуникации |
|||
Трофимов П. А. |
Английский язык |
|||
Иванова Е. А. |
Английский язык |
|||
Уткина Н. В. |
Английский язык |
|||
Вывести список лентяев, имеющих несколько двоек.
SELECT DISTINCT R1.ФИО
FROM R1 a, R1 b
WHERE a.ФИО = b.ФИО AND
a.Дисциплина <> b.Дисциплина AND
a.Оценка <= 2 AND b.Оценка <= 2;
Здесь мы использовали псевдонимы для именования отношения R1 a и b, так как для записи условий поиска нам необходимо работать сразу с двумя экземплярами данного отношения.
Результат:
ФИО
Степанова К. Е.
Из этих примеров хорошо видно, что логика работы оператора выбора
(декартово произведение—селекция—проекция) не совпадает с порядком описания в нем данных (сначала список полей для проекции, потом список таблиц для декартова произведения, потом условие соединения). Дело в том, что SQL изначально разрабатывался для применения конечными пользователями, и его стремились сделать возможно ближе к языку естественному, а не к языку алгоритмическому. По этой причине SQL на первых порах вызывает путаницу и раздражение у начинающих его изучать профессиональных программистов, которые привыкли разговаривать с машиной именно на алгоритмических языках.
Наличие неопределенных (Null) значений повышает гибкость обработки информации, хранящейся в БД. В наших примерах мы можем предположить ситуацию, когда студент пришел на экзамен, но не сдавал его по некоторой причине, в этом случае оценка по некоторой дисциплине для данного студента имеет неопределенное значение. В данной ситуации можно поставить вопрос: «Найти студентов, пришедших на экзамен, но не сдававших его с указанием названия дисциплины». Оператор SELECT будет выглядеть следующим образом:
SELECT ФИО, Дисциплина
FROM R1
WHERE Оценка IS NULL
Результат:
ФИО Дисциплина
Миронов А. В. Теория информации
7.5.Применение агрегатных функций и вложенных запросов
ВSQL добавлены дополнительные функции, которые позволяют вычис-
лять обобщенные групповые значения. Для применения агрегатных функций предполагается предварительная операция группировки. В чем состоит суть операции группировки? При группировке все множество кортежей отношения разбивается на группы, в которых собираются кортежи, имеющие одинаковые значения атрибутов, которые заданы в списке группировки.
Например, сгруппируем отношение R1 по значению столбца Дисциплина. Мы получим 4 группы, для которых можем вычислить некоторые групповые значения, например, количество кортежей в группе, максимальное или минимальное значение столбца Оценка.
Это делается с помощью агрегатных функций. Агрегатные функции вы-
числяют одиночное значение для всей группы таблицы. Список этих функций представлен в табл. 7.7.
Таблица 7.7. Агрегатные функции |
||
Функция |
Результат |
|
COUNT |
Количество строк или непустых значений полей, которые вы- |
|
брал запрос |
||
SUM |
Сумма всех выбранных значений данного поля |
|||
AVG |
Среднеарифметическое значение всех выбранных значений |
|||
данного поля |
||||
MIN |
Наименьшее из всех выбранных значений данного поля |
|||
MAX |
Наибольшее из всех выбранных значений данного поля |
|||
R1 |
|||
ФИО |
Дисциплина |
Оценка |
|
Группа |
Петров Ф. И. |
Базы данных |
5 |
1 |
Сидоров К. А. |
Базы данных |
4 |
Миронов А. В. |
Базы данных |
2 |
|
Степанова К. Е. Базы данных |
2 |
||
Крылова Т. С. |
Базы данных |
5 |
|
Владимиров В. |
Базы данных |
5 |
|
А. |
|||
Группа |
Сидоров К. А. |
Теория информации |
4 |
2 |
Степанова К. Е. Теория информации |
2 |
|
Крылова Т. С. |
Теория информации |
5 |
|
Миронов А. В. |
Теория информации |
Null |
|
Группа |
Трофимов П. |
Сети и телекоммуникации |
4 |
3 |
А. |
||
Иванова Е. А. |
Сети и телекоммуникации |
5 |
|
Уткина Н. В. |
Сети и телекоммуникации |
5 |
|
Группа |
Владимиров В. |
Английский язык |
4 |
4 |
А. |
||
Трофимов П. |
Английский язык |
5 |
|
А. |
|||
Иванова Е. А. |
Английский язык |
3 |
|
Петров Ф. И. |
Английский язык |
5 |
Агрегатные функции используются подобно именам полей в операторе SELECT, но с одним исключением: они берут имя поля как аргумент. С функциями SUM и AVG могут использоваться только числовые поля. С функциями COUNT, MAX и MIN могут использоваться как числовые, так и символьные поля. При использовании с символьными полями MAX и MIN будут транслировать их в эквивалент ASCII кода и обрабатывать в алфавитном порядке. Некоторые СУБД позволяют использовать вложенные агрегаты, но это является отклонением от стандарта ANSI со всеми вытекающими отсюда последствиями.
Например, можно вычислить количество студентов, сдававших экзамены по каждой дисциплине. Для этого надо выполнить запрос с группировкой по полю «Дисциплина» и вывести в качестве результата название дисциплины и количество строк в группе по данной дисциплине. Применение символа * в качестве аргумента функции COUNT означает подсчет всех строк в группе.
SELECT R1.Дисциплина, COUNT(*) FROM R1
GROUP BY R1.Дисциплина
Результат:
Дисциплина |
COUNT(*) |
Базы данных |
6 |
Теория информации |
4 |
Сети и телекоммуникации 3
Английский язык |
4 |
|||
Если же мы хотим сосчитать количество сдавших экзамен по какой-либо дисциплине, то нам необходимо исключить неопределенные значения из исходного отношения перед группировкой. В этом случае запрос будет выглядеть следующим образом:
SELECT R1.Дисциплина, COUNT(*)
FROM R1
WHERE R1.Оценка IS NOT NULL
GROUP BY R1.Дисциплина
Получим результат:
Дисциплина COUNT(*)
Базы данных |
6 |
||||
Теория информации |
3 |
||||
Сети и телекоммуникации |
3 |
||||
Английский язык |
4 |
||||
В этом случае строка со студентом |
|||||
Миронов А. В. |
Теория информации |
Null |
|||
не попадет в набор кортежей перед группировкой, поэтому количество кортежей в группе для дисциплины «Теория информации» будет на 1 меньше.
Можно применять агрегатные функции также и без операции предварительной группировки, в этом случае все отношение рассматривается как одна группа и для этой группы можно вычислить одно значение на группу.
Обратившись снова к базе данных «Сессия» (таблицы R1, R2, R3 ), найдем количество успешно сданных экзаменов:
SELECT COUNT(*) FROM R1
WHERE Оценка > 2;
Это, конечно, отличается от выбора поля, поскольку всегда возвращается одиночное значение, независимо от того, сколько строк находится в таблице. Аргументом агрегатных функций могут быть отдельные столбцы таблиц. Но для того, чтобы вычислить, например, количество различных значений некоторого столбца в группе, необходимо применить ключевое слово DISTINCT совместно с именем столбца. Вычислим количество различных оценок, полученных по каждой дисциплине:
SELECT R1.Дисциплина, COUNT(DISTINCT R1.Оценка)
FROM R1
WHERE R1.Оценка IS NOT NULL
GROUP BY R1.Дисциплина
Результат:
Дисциплина |
COUNT(DISTINCT R1.Оценка) |
Базы данных |
3 |
Теория информации |
3 |
Сети и телекоммуникации 2
Английский язык |
3 |
|||
В результат можно включить значение поля группировки и несколько агрегатных функций, а в условиях группировки можно использовать несколько полей. При этом группы образуются по набору заданных полей группировки. Операции с агрегатными функциями могут быть применены к объединению множества исходных таблиц. Например, поставим вопрос: определить для каждой группы и каждой дисциплины количество успешно сдавших экзамен и средний балл по дисциплине.
SELECT R1.Оценка, R1.Дисциплина, COUNT(*), AVR(Оценка)
FROM R1,R2
WHERE R1.ФИО = R2.ФИО AND
R1.Оценка IS NOT NULL AND
R1.Оценка > 2
GROUP BY R1.Оценка R1.Дисциплина
Результат:
Дисциплина |
COUNT(*) |
AVR(Oцeнкa) |
Базы данных |
6 |
3.83 |
Теория информации |
3 |
3.67 |
Сети и телекоммуникации |
3 |
4.66 |
Английский язык |
4 |
4.25 |
Мы не можем использовать агрегатные функции в предложении WHERE, потому что предикаты оцениваются в терминах одиночной строки, а агрегатные функции — в терминах групп строк.
Предложение GROUP BY позволяет определять подмножество значений в особом поле в терминах другого поля и применять функцию агрегата к подмножеству. Это дает возможность объединять поля и агрегатные функции в едином предложении SELECT. Агрегатные функции могут применяться как в выражении вывода результатов строки SELECT, так и в выражении условия обработки сформированных групп HAVING. В этом случае каждая агрегатная функция вычисляется для каждой выделенной группы. Значения, полученные при вычислении агрегатных функций, могут быть использованы для вывода соответствующих результатов или для условия отбора групп.
Построим запрос, который выводит группы, в которых по одной дисциплине на экзаменах получено больше одной двойки:
SELECT R2.Группа FROM R1,R2
WHERE R1.ФИО = R2.ФИО AND
R1.Оценка = 2
GROUP BY R2.Группа, R1.Дисциплина
HAVING count(*)> 1
Вдальнейшем в качестве примера будем работать не с БД «Сессия», а
сБД «Банк», состоящей из одной таблицы F, в которой хранится отношение F, содержащее информацию о счетах в филиалах некоторого банка:
F = (N, ФИО, Филиал, ДатаОткрытия, ДатаЗакрытия, Остаток); Q = (Филиал, Город);
поскольку на этой базе можно ярче проиллюстрировать работу с агрегатными функциями и группировкой.
Например, предположим, что мы хотим найти суммарный остаток на счетах в филиалах. Можно сделать раздельный запрос для каждого из них, выбрав SUM(Остаток) из таблицы для каждого филиала. GROUP BY, однако, позволит поместить их все в одну команду:
SELECT Филиал, SUM(Остаток) FROM F
GROUP BY Филиал;
GROUP BY применяет агрегатные функции независимо для каждой группы, определяемой с помощью значения поля Филиал. Группа состоит из строк с одинаковым значением поля Филиал, и функция SUM применяется отдельно для каждой такой группы, то есть суммарный остаток на счетах подсчитывается отдельно для каждого филиала. Значение поля, к которому применяется GROUP BY, имеет, по определению, только одно значение на группу вывода, как и результат работы агрегатной функции. Поэтому мы можем совместить в одном запросе агрегат и поле. Вы можете также использовать GROUP BY с несколькими полями.
Предположим, что мы хотели бы увидеть только те суммарные значения остатков на счетах, которые превышают $5000. Чтобы увидеть суммарные остатки свыше $5000, необходимо использовать предложение HAVING. Предложение HAVING определяет критерии, используемые, чтобы удалять определенные группы из вывода, точно так же как предложение WHERE делает это для индивидуальных строк.
Правильной командой будет следующая:
SELECT Филиал, SUM(Остаток)
FROM F
GROUP BY Филиал
HAVING SUM(Остаток) > 5000;
Аргументы в предложении HAVING подчиняются тем же самым правилам, что и в предложении SELECT, где используется GROUP BY. Они должны иметь одно значение на группу вывода.
Следующая команда будет запрещена:
SELECT Филиал,SUM(Остаток) FROM F
GROUP BY Филиал
HAVING ДатаОткрытия = 27/12/1999;
Поле ДатаОткрытия не может быть использовано в предложении HAVING, потому что оно может иметь больше чем одно значение на группу
вывода. Чтобы избежать такой ситуации, предложение HAVING должно ссылаться только на агрегаты и поля, выбранные GROUP BY. Имеется правильный способ сделать вышеупомянутый запрос:
SELECT Филиал,SUM(Остаток) FROM F
WHERE ДатаОткрытия = ’27/12/1999′ GROUP BY Филиал;
Смысл данного запроса следующий: найти сумму остатков по каждому филиалу счетов, открытых 27 декабря 1999 года.
Как и говорилось ранее, HAVING может использовать только аргументы, которые имеют одно значение на группу вывода. Практически, ссылки на агрегатные функции — наиболее общие, но и поля, выбранные с помощью GROUP BY, также допустимы. Например, мы хотим увидеть суммарные остатки на счетах филиалов в Санкт-Петербурге, Пскове и Урюпинске:
SELECT Филиал, SUM(Остаток) FROM F,Q
WHERE F.Филиал = Q.Филиал
GROUP BY Филиал
HAVING Город IN («Санкт-Петербург», «Псков», «Урюпинск»);
Поэтому в арифметических выражениях предикатов, входящих в условие выборки раздела HAVING, прямо можно использовать только спецификации столбцов, указанных в качестве столбцов группирования в разделе GROUP BY. Остальные столбцы можно специфицировать только внутри спецификаций агрегатных функций COUNT, SUM, AVG, MIN и MAX, вычисляющих в данном случае некоторое агрегатное значение для всей группы строк. Аналогично обстоит дело с подзапросами, входящими в предикаты условия выборки раздела HAVING: если в подзапросе используется характеристика текущей группы, то она может задаваться только путем ссылки на столбцы группирования.
Результатом выполнения раздела HAVING является сгруппированная таблица, содержащая только те группы строк, для которых результат вычисления условия поиска есть TRUE. В частности, если раздел HAVING присутствует в табличном выражении, не содержащем GROUP BY, то результатом его выполнения будет либо пустая таблица, либо результат выполнения предыдущих разделов табличного выражения, рассматриваемый как одна группа без столбцов группирования.
7.6. Вложенные запросы
Теперь вернемся к БД «Сессия» и рассмотрим на ее примере использование вложенных запросов.
С помощью SQL можно вкладывать запросы внутрь друг друга. Обычно внутренний запрос генерирует значение, которое проверяется в предикате внешнего запроса (в предложении WHERE или HAVING ), определяющего, верно оно или нет. Совместно с подзапросом можно использовать предикат EXISTS, который возвращает истину, если вывод подзапроса не пуст.
В сочетании с другими возможностями оператора выбора, такими как группировка, подзапрос представляет собой мощное средство для достижения нужного-результата. В части FROM оператора SELECT допустимо применять синонимы к именам таблицы, если при формировании запроса нам требуется
более чем один экземпляр некоторого отношения. Синонимы задаются с использованием ключевого слова AS, которое может быть вообще опущено. Поэтому часть FROM может выглядеть следующим образом:
FROM R1 AS A, R1 AS B
или
FROM R1 A, R1 B;
оба выражения эквивалентны и рассматриваются как применения оператора SELECT к двум экземплярам таблицы R1.
Например, покажем, как выглядят на SQL некоторые запросы к БД «Сес-
сия»:
Список тех, кто сдал все положенные экзамены.
SELECT ФИО
FROM R1 as a
WHERE Оценка > 2
GROUP BY ФИО
HAVING COUNT(*) = (SELECT COUNT(*)
FROM R2,R3
WHERE R2.Группа=R3.Группа AND ФИО=a.ФИО)
Здесь во встроенном запросе определяется общее число экзаменов, которые должен сдавать каждый студент, обучающийся в группе, в которой учится данный студент, и это число сравнивается с числом экзаменов, которые сдал данный студент.
Список тех, кто должен был сдавать экзамен по БД, но пока еще не сдавал.
SELECT ФИО
FROM R2 a, R3
WHERE R2.Группа=R3.Группа AND Дисциплина = «БД» AND NOT EXISTS (SELECT ФИО
FROM R1
WHERE ФИО=a.ФИО AND Дисциплина = «БД»)
Предикат EXISTS (SubQuery) истинен, когда подзапрос SubQuery не пуст, то есть содержит хотя бы один кортеж, в противном случае предикат EXISTS ложен.
Предикат NOT EXISTS обратно — истинен только тогда, когда подзапрос SubQuery пуст.
Обратите внимание, каким образом NOT EXISTS с вложенным запросом позволяет обойтись без операции разности отношений. Например, формулировка запроса со словом «все» может быть выполнена как бы с двойным отрицанием. Рассмотрим пример базы, которая моделирует поставку отдельных деталей отдельными поставщиками, она представлена одним отношением SP «Поставщики—детали» со схемой
SP (Номер_поставщика, номер_детали) P (номер_детали, наименование)
Вот каким образом формулируется ответ на запрос: «Найти поставщиков, которые поставляют все детали».
SELECT DISTINCT НОМЕР_ПОСТАВЩИКА FROM SP SP1 WHERE NOT EX-
ISTS
(SELECT номер_детали FROM P WHERE NOT EXISTS (SELECT * FROM SP SP2
Соседние файлы в предмете Информатика
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
Задача под номером /*6*/
/*1*/
CREATE TABLE STIP
(
IDD INT,
FIO VARCHAR2(200),
SUMMA INTEGER,
DATA_STIP DATE);
SELECT * FROM STIP;
CREATE TABLE PROVERKA
(
Dd INT ,
FIO VARCHAR2(200),
DISP VARCHAR2(200),
OCENKA VARCHAR2(200) CHECK (OCENKA IN (‘ОТЛИЧНО’,’ХОРОШО’,’УДОВЛЕТ’,’НУДОВЛ’)),
DATA_OCENKA DATE);
SELECT * FROM PROVERKA;
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11111,’Иванов Иван Иванович’,’Базы Данных’,’ОТЛИЧНО’,’10.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11112,’Сидоров Иван Гаврилович’,’Базы Данных’,’ХОРОШО’,’10.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11113,’Сидоров Иван Гаврилович’,’ТПР’,’ОТЛИЧНО’,’14.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11114,’Петров Алексей Иванович’,’Моделирование систем’,’УДОВЛЕТ’,’18.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11115,’Иванов Иван Иванович’,’ТПР’,’ОТЛИЧНО’,’10.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11116,’Иванов Иван Иванович’,’Моделирование систем’,’ХОРОШО’,’10.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11117,’Петров Алексей Иванович’,’ТПР’,’ОТЛИЧНО’,’14.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11118,’Петров Алексей Иванович’,’Базы Данных’,’ОТЛИЧНО’,’18.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11119,’Сидоров Иван Гаврилович’,’Моделирование систем’,’ОТЛИЧНО’,’14.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11120,’Иванов Иван Иванович’,’Философия’,’ОТЛИЧНО’,’10.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11121,’Алиев Иван Иванович’,’Моделирование систем’,’ХОРОШО’,’10.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11122,’Мальцев Алексей Иванович’,’ТПР’,’ОТЛИЧНО’,’14.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11123,’Мальцев Алексей Иванович’,’Базы Данных’,’ОТЛИЧНО’,’18.01.12′);
INSERT INTO PROVERKA(Dd,FIO,DISP,OCENKA,DATA_OCENKA) VALUES (11124,’Мальцев Алексей Иванович’,’Моделирование систем’,’ОТЛИЧНО’,’14.01.12′);
/*2*/
INSERT INTO STIP(FIO)
SELECT DISTINCT FIO
FROM PROVERKA;
/* Расчёт суммы стипендиии за каждую оценку, почти каждую
SELECT 1250*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Сидоров Иван Гаврилович’ AND PROVERKA.OCENKA=’ОТЛИЧНО’;
SELECT 800*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Сидоров Иван Гаврилович’ AND PROVERKA.OCENKA=’ХОРОШО’;
SELECT 1250*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Петров Алексей Иванович’ AND PROVERKA.OCENKA=’ОТЛИЧНО’;
SELECT 800*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Петров Алексей Иванович’ AND PROVERKA.OCENKA=’ХОРОШО’;
SELECT 300*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Петров Алексей Иванович’ AND PROVERKA.OCENKA=’УДОВЛЕТ’;
SELECT 1250*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Иванов Иван Иванович’ AND PROVERKA.OCENKA=’ОТЛИЧНО’;
SELECT 800*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Иванов Иван Иванович’ AND PROVERKA.OCENKA=’ХОРОШО’;
*/
/*3*/
UPDATE STIP
SET SUMMA=(SELECT 1250*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Сидоров Иван Гаврилович’ AND PROVERKA.OCENKA=’ОТЛИЧНО’)+(SELECT 800*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Сидоров Иван Гаврилович’ AND PROVERKA.OCENKA=’ХОРОШО’)+(SELECT 300*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Сидоров Иван Гаврилович’ AND PROVERKA.OCENKA=’УДОВЛЕТ’)
WHERE STIP.FIO=’Сидоров Иван Гаврилович’;
UPDATE STIP
SET SUMMA=(SELECT 1250*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Петров Алексей Иванович’ AND PROVERKA.OCENKA=’ОТЛИЧНО’)+(SELECT 800*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Петров Алексей Иванович’ AND PROVERKA.OCENKA=’ХОРОШО’)+(SELECT 300*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Петров Алексей Иванович’ AND PROVERKA.OCENKA=’УДОВЛЕТ’)
WHERE STIP.FIO=’Петров Алексей Иванович’;
UPDATE STIP
SET SUMMA=(SELECT 1250*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Иванов Иван Иванович’ AND PROVERKA.OCENKA=’ОТЛИЧНО’)+(SELECT 800*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Иванов Иван Иванович’ AND PROVERKA.OCENKA=’ХОРОШО’)+(SELECT 300*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Иванов Иван Иванович’ AND PROVERKA.OCENKA=’УДОВЛЕТ’)
WHERE STIP.FIO=’Иванов Иван Иванович’;
UPDATE STIP
SET SUMMA=(SELECT 1250*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Мальцев Алексей Иванович’ AND PROVERKA.OCENKA=’ОТЛИЧНО’)+(SELECT 800*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Мальцев Алексей Иванович’ AND PROVERKA.OCENKA=’ХОРОШО’)+(SELECT 300*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Мальцев Алексей Иванович’ AND PROVERKA.OCENKA=’УДОВЛЕТ’)
WHERE STIP.FIO=’Мальцев Алексей Иванович’;
UPDATE STIP
SET SUMMA=(SELECT 1250*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Алиев Иван Иванович’ AND PROVERKA.OCENKA=’ОТЛИЧНО’)+(SELECT 800*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Алиев Иван Иванович’ AND PROVERKA.OCENKA=’ХОРОШО’)+(SELECT 300*COUNT(PROVERKA.OCENKA)
FROM PROVERKA
WHERE PROVERKA.FIO=’Алиев Иван Иванович’ AND PROVERKA.OCENKA=’УДОВЛЕТ’)
WHERE STIP.FIO=’Алиев Иван Иванович’;
/*4*/
ALTER TABLE STIP ADD PRIMECH VARCHAR2(200);
/*5*/
SELECT FIO,COUNT(PROVERKA.DISP) AS СКОЛЬКО
FROM PROVERKA
GROUP BY FIO
HAVING PROVERKA.FIO=’Иванов Иван Иванович’;
SELECT FIO,COUNT(PROVERKA.DISP) AS СКОЛЬКО
FROM PROVERKA
GROUP BY FIO
HAVING PROVERKA.FIO=’Петров Алексей Иванович’;
SELECT FIO,COUNT(PROVERKA.DISP) AS СКОЛЬКО
FROM PROVERKA
GROUP BY FIO
HAVING PROVERKA.FIO=’Сидоров Иван Гаврилович’;
SELECT FIO,COUNT(PROVERKA.DISP) AS СКОЛЬКО
FROM PROVERKA
GROUP BY FIO
HAVING PROVERKA.FIO=’Мальцев Алексей Иванович’;
SELECT FIO,COUNT(PROVERKA.DISP) AS СКОЛЬКО
FROM PROVERKA
GROUP BY FIO
HAVING PROVERKA.FIO=’Алиев Иван Иванович’;
SELECT FIO,COUNT(PROVERKA.DISP) AS СКОЛЬКО
FROM PROVERKA
GROUP BY FIO;
/*6*/
SELECT * FROM STIP;
SELECT * FROM PROVERKA;
UPDATE STIP
SET PRIMECH=’ПЕРЕСЧИТАТЬ СТИПЕНДИЮ’
WHERE STIP.FIO = (SELECT DISTINCT FIO FROM PROVERKA WHERE (SELECT COUNT(DISTINCT DISP) FROM PROVERKA) IN (SELECT COUNT(PROVERKA.DISP)FROM PROVERKA GROUP BY FIO));
/*7.1*/
UPDATE STIP
SET SUMMA=SUMMA*0.02+SUMMA
WHERE PRIMECH=’ПЕРЕСЧИТАТЬ СТИПЕНДИЮ’;
UPDATE STIP
SET DATA_STIP=SYSDATE
WHERE PRIMECH=’ПЕРЕСЧИТАТЬ СТИПЕНДИЮ’;
/*7.2*/
Приведем несколько примеров использования оператора SELECT.
- Вывести список всех групп (без повторений), где должны пройти экзамены.
SELECT DISTINCT Группы FROM R3
Результат:
- Вывести список студентов, которые сдали экзамен по дисциплине «Базы данных» на «отлично».
SELECT ФИО FROM R1 WHERE Дисциплина = "Базы данных" AND Оценка = 5
Результат:
ФИО Петров Ф. И. Крылова Т. С. Владимирова В. А. - Вывести список всех студентов, которым надо сдавать экзамены с указанием названий дисциплин, по которым должны проводиться эти экзамены.
SELECT ФИО,Дисциплина FROM R2,R3 WHERE R2.Группа = R3.Группа;
Здесь часть WHERE задает условия соединения отношений R2 и R3, при отсутствии условий соединения в части WHERE результат будет эквивалентен расширенному декартову произведению, и в этом случае каждому студенту были бы приписаны все дисциплины из отношения R3, а не те, которые должна сдавать его группа.
Результат:
ФИО Дисциплина Петров Ф. И. Базы данных Сидоров К. А. Базы данных Миронов А. В. Базы данных Степанова К. Е. Базы данных Крылова Т. С. Базы данных Владимиров В. А. Базы данных Петров Ф. И. Теория информации Сидоров К. А. Теория информации Миронов А. В. Теория информации Степанова К. Е. Теория информации Крылова Т. С. Теория информации Владимиров В. А. Теория информации Петров Ф. И. Английский язык Сидоров К. А. Английский язык Миронов А. В. Английский язык Степанова К. Е. Английский язык Крылова Т. С. Английский язык Владимиров В. А. Английский язык Трофимов П. А. Сети и телекоммуникации Иванова Е. А. Сети и телекоммуникации Уткина Н. В. Сети и телекоммуникации Трофимов П. А. Английский язык Иванова Е. А. Английский язык Уткина Н. В. Английский язык - Вывести список лентяев, имеющих несколько двоек.
SELECT DISTINCT R1.ФИО FROM R1 a, R1 b WHERE a.ФИО = b.ФИО AND a.Дисциплина <> b.Дисциплина AND a.Оценка <= 2 AND b.Оценка <= 2;
Здесь мы использовали псевдонимы для именования отношения R1 a и b, так как для записи условий поиска нам необходимо работать сразу с двумя экземплярами данного отношения.
Результат:
Из этих примеров хорошо видно, что логика работы оператора выбора (декартово произведение—селекция—проекция) не совпадает с порядком описания в нем данных (сначала список полей для проекции, потом список таблиц для декартова произведения, потом условие соединения). Дело в том, что SQL изначально разрабатывался для применения конечными пользователями, и его стремились сделать возможно ближе к языку естественному, а не к языку алгоритмическому. По этой причине SQL на первых порах вызывает путаницу и раздражение у начинающих его изучать профессиональных программистов, которые привыкли разговаривать с машиной именно на алгоритмических языках.
Наличие неопределенных ( Null ) значений повышает гибкость обработки информации, хранящейся в БД. В наших примерах мы можем предположить ситуацию, когда студент пришел на экзамен, но не сдавал его по некоторой причине, в этом случае оценка по некоторой дисциплине для данного студента имеет неопределенное значение. В данной ситуации можно поставить вопрос: «Найти студентов, пришедших на экзамен, но не сдававших его с указанием названия дисциплины». Оператор SELECT будет выглядеть следующим образом:
SELECT ФИО, Дисциплина FROM R1 WHERE Оценка IS NULL
Результат:
ФИО | Дисциплина |
---|---|
Миронов А. В. | Теория информации |
В текстовом файле есть ведомость результатов сдачи экзаменов студенческой группы. Ведомость содержит для каждого студента фамилию, имя отчество и оценки по пяти предметам. Студентов в группе не более 20 человек.
Написать программу, которая предоставляет следующую информацию:
- список студентов (ФИО);
- список студентов, которые сдали все экзамены только на 5;
- список студентов, которые имеют хотя-бы одну тройку по экзаменам;
- список студентов, у которых есть двойки. Если студент, имеет более чем одну двойку, он исключается из списка.
Задачу решил Naikon, он, как всегда, впереди всех. Собственно, вот код:
#include <iostream> #include <fstream> #include <windows.h> #include <vector> using namespace std; struct Student { void fn_student_list()///Выводим список студентов { for(unsigned int i=0;i<vec_FIO.size();++i)cout<<vec_FIO[i]<<endl; } vector<string>vec_FIO; vector<int> vec_math,vec_physics,vec_chemistry,vec_geometry,vec_rus; void fn_student_exam_5()///Список студентов которые сдали все экзамены только на пять { bool five=false; for(unsigned int i=0;i<vec_FIO.size();++i) { unsigned int sum=0; if(vec_math[i]==5)sum+=5; if(vec_physics[i]==5)sum+=5; if(vec_chemistry[i]==5)sum+=5; if(vec_geometry[i]==5)sum+=5; if(vec_rus[i]==5)sum+=5; if(sum==25) { cout<<vec_FIO[i]<<endl; five=true; } } if(five==false)cout<<"Таких студентов нет :("<<endl; } void fn_student_exam_3()///Список студентов которые имеют хотя-бы одну тройку по экзаменам { bool three=false; for(unsigned int i=0;i<vec_FIO.size();++i) { unsigned int sum=0; if(vec_math[i]==3)sum++; if(vec_physics[i]==3)sum++; if(vec_chemistry[i]==3)sum++; if(vec_geometry[i]==3)sum++; if(vec_rus[i]==3)sum++; if(sum>=1) { cout<<vec_FIO[i]<<endl; three=true; } } if(three==false)cout<<"Таких студентов нет :("<<endl; } void fn_student_exam_2()///Список студентов у которых есть двойки { bool two=false; for(unsigned int i=0;i<vec_FIO.size();++i)///удаляем студентов у котороыъ больше одной двойки { int sum=0; if(vec_math[i]==2)sum++; if(vec_physics[i]==2)sum++; if(vec_chemistry[i]==2)sum++; if(vec_geometry[i]==2)sum++; if(vec_rus[i]==2)sum++; if(sum>=2) { vec_FIO.erase(vec_FIO.begin()+i); vec_math.erase(vec_math.begin()+i); vec_physics.erase(vec_physics.begin()+i); vec_chemistry.erase(vec_chemistry.begin()+i); vec_geometry.erase(vec_geometry.begin()+i); vec_rus.erase(vec_rus.begin()+i); } } for(unsigned int i=0;i<vec_FIO.size();++i)///Выводим список студентов { unsigned int sum=0; if(vec_math[i]==2)sum++; if(vec_physics[i]==2)sum++; if(vec_chemistry[i]==2)sum++; if(vec_geometry[i]==2)sum++; if(vec_rus[i]==2)sum++; if(sum>=1) { cout<<vec_FIO[i]<<endl; two=true; } } if(two==false)cout<<"Таких студентов нет :("<<endl; } }; int main() { Student student; string str,fstr; SetConsoleOutputCP(1251); int value,math,physics,chemistry,geometry,rus,number_of_students=0; ifstream in("file.txt"); if(!in)///Если не смогли открыть файл { cout<<"Can't open filen"; return 1; } getline(in,str); while(in>>str) { fstr+=str+' '; for(int i=0;i<2;++i) { in>>str; fstr+=str+' '; } student.vec_FIO.push_back(fstr); fstr.clear(); in>>math; student.vec_math.push_back(math); in>>physics; student.vec_physics.push_back(physics); in>>chemistry; student.vec_chemistry.push_back(chemistry); in>>geometry; student.vec_geometry.push_back(geometry); in>>rus; student.vec_rus.push_back(rus); number_of_students++; if(number_of_students==20)break; } in.close();///Закрыли файл start:cout<<"n1. Список студентов (ФИО)"<<endl; cout<<"2. Список студентов которые сдали все экзамены только на пять."<<endl; cout<<"3. Список студентов которые имеют хотя-бы одну тройку по экзаменам."<<endl; cout<<"4. Список студентов у которых есть двойки."<<endl; cout<<"(если студент имеет больше одной двойки он исключается из списка)."<<endl; cout<<"5. Введите 'q' для выхода"<<endl; cout<<"Пожалуйста сделайте свой выбор: "; char c; cin>>c; switch(c) { case '1': { student.fn_student_list(); goto start; } case '2': { student.fn_student_exam_5(); goto start; } case '3': { student.fn_student_exam_3(); goto start; } case '4': { student.fn_student_exam_2(); goto start; } case 'q': { return 0; } default: { cout<<"Неверный ввод :( Попробуйте сноваn"; goto start; } } return 0; }
Как я успел увидеть, код платформо-зависимый, обратите внимание на заголовки, строка 3, там подключен файл <windows.h>. То есть, под линуксом уже такую программу сразу не скомпилируешь. Но из этой ситуации можно легко выбраться. В программе функция SetConsoleOutputCP(1251); для установки кодировки cp-1251, это необходимо, если в консоли появляются каракули вместо символов русского алфавита. Но в линуксе с отображением кириллицы в консоли все в порядке, а значит и эта функция не нужна. Так что, те, кто на линуксе работают, перед компиляцией программы, смело комментируйте строки 3 и 95, они вам не нужны.
Входные данные из файла:
- Ф.И.О Алгебра Физика Химия Геометрия Русс. язык
- Иванов И. И. 3 4 4 4 5
- Карпов И. С. 5 5 5 5 3
- Петров С. К. 3 3 4 4 3
- Борзенков А. А. 4 4 4 4 4
- Константинов П. П. 5 5 5 5 5
Смотри на результат работы программы:
Задача:
База данных “Сессия” содержит факты, которые описывают отношения двух типов:
1) lector(<фамилия>,<дисциплина>,<номер группы>,<дата экзамена>
2) student(<фамилия>,<номер группы>,<номер зачетки>).
В работе необходимо выполнить следующие действия:
1. Создать с помощью фактов базу данных «Сессия», включающую предикаты lector
и student
.
2. Написать правила, обеспечивающие ответ на следующие вопросы:
А) Выдать на экран фамилии всех студентов, которые сдают экзамен определенного числа, дата задается следующим образом: ‘хх.хх.xxxx’.
Б) Выдать на экран фамилии всех студентов, которые сдают экзамен определенному лектору.
В) Выдать на экран названия дисциплин, по которым будет сдавать экзамен определенный студент.
Г) Выдать на экран фамилии преподавателей, которые принимают экзамены в данной группе.
Д) Выдать на экран фамилии всех студентов, которые учатся в данной группе.
Е) Выдать на экран названия дисциплин, по которым принимает экзамен данный преподаватель.
Решение:
База данных заполнена следующими фактами:
lector('Адамов', 'программирование', 'А1', '11.04.2021'). lector('Адамов', 'базы данных', 'А2', '14.04.2021'). lector('Акинфеев', 'системное программирование', 'А2', '15.04.2021'). lector('Акинфеев', 'основы программирования', 'А1', '16.04.2021'). lector('Алвин', 'компьютерная графика', 'В1', '17.04.2021'). lector('Алвин', 'психология', 'А1', '11.04.2021'). lector('Алдонин', 'философия', 'В1', '11.04.2021'). lector('Аленичев', 'микропроцессорные системы', 'В2', '15.04.2021'). lector('Ананко', 'физическая культура и спорт', 'В3', '16.04.2021'). lector('Анюков', 'математический анализ', 'А2', '17.04.2021'). student('Арифуллин', 'А1', 423537665478546). student('Аршавин', 'А1', 423537665478547). student('Афанасьев', 'А1', 423537665478548). student('Ахметов', 'А2', 423537665478549). student('Баженов', 'А2', 423537665478550). student('Бакаев', 'А2', 423537665478551). student('Баринов', 'А2', 423537665478552). student('Бахарев', 'В1', 423537665478553). student('Безродный', 'В1', 423537665478554). student('Беляев', 'В1', 423537665478555). student('Березуцкий', 'В1', 423537665478556). student('Бесчастных', 'В1', 423537665478557). student('Билялетдинов', 'В2', 423537665478558). student('Бобёр', 'В2', 423537665478559). student('Боков', 'В2', 423537665478560). student('Борзенков', 'В2', 423537665478561). student('Бородюк', 'В2', 423537665478562). student('Бояринцев', 'В3', 423537665478563). student('Бугаев', 'В3', 423537665478564). student('Будянский', 'В3', 423537665478565). student('Бузникин', 'В3', 423537665478566).
Получить всех студентов можно так:
student(X, Y, Z).
Фрагмент результата:
А информацию обо всех лекторах (экзаменах) — так:
lector(A, B, C, D).
Фрагмент результата:
А) Чтобы найти студентов, сдающих экзамен определенного числа (например 11.04.2021) — надо перебрать лекторов, найти группы сдающие в этот день и потом перебрать студентов этих групп:
Дата = '11.04.2021', lector(Имя, Предмет, Группа, Дата), student(Студент, Группа, _Номер).
Результат выполнения:
Б) Чтобы найти студентов, сдающих экзамен определенному лектору (например Адамову) — надо сначала перебрать лекторов, найти группы у которых он принимает экзамен, а затем — перебрать студентов этих групп. Цель может выглядеть так:
Имя = 'Адамов', lector(Имя, Предмет, Группа, Дата), student(Студент, Группа, _Номер).
В) чтобы найти предметы, которые надо сдать студенту — перебираем студентов чтобы найти ЭТОГО конкретного и узнать его группу. А затем — смотрим в таблице лекторов какие предметы сдает эта группа. Примеры целей:
?- Студент = 'Бахарев', lector(_Лектор, Предмет, Группа, Дата), student(Студент, Группа, _Номер). ?- Студент = 'Арифуллин', lector(_Лектор, Предмет, Группа, Дата), student(Студент, Группа, _Номер).
Г) Чтобы найти преподавателей принимающих у заданной группы, перебираем преподавателей, сверяем группу:
Группа = 'А2', lector(Лектор, Предмет, Группа, Дата).
Д) Чтобы найти всех студентов в заданной группе — перебираем студентов, сверяем группу:
Группа = 'А1', student(Студент, Группа, _Номер).
Е) Чтобы найти все предметы данного преподавателя — перебираем преподавателей, сверяем предмет:
Лектор = 'Акинфеев', lector(Лектор, Предмет, Группа, Дата).
using System; using System.Collections.Generic; using System.Linq; ... static void Main(string[] args) { List<Student> students = new List<Student>(); Random rnd = new Random(); for (int i = 0; i < 25; i++) { int[] balls = new int[4]; for (int j = 0; j < 4; j++) { balls[j] = rnd.Next(2, 6); } students.Add(new Student(i.ToString(), (i + i).ToString(), balls)); } IEnumerable<Student> queryStudents = from student in students where student.Average >= 4.0 select student; foreach (Student student in queryStudents) { Console.WriteLine(student.Name + " : " + student.Surname + " : " + student.Average); } Console.ReadKey(); }