/db3

Отчет по лр 3 исбд

Лабораторная работа №3

Задание

По варианту, выданному преподавателем, составить и выполнить запросы к базе данных "Учебный процесс". Вариант: 7712

Отчёт по лабораторной работе должен содержать:

  1. Текст задания.
  2. Реализацию запросов на SQL.
  3. Выводы по работе.

Темы для подготовки к защите лабораторной работы:

  1. SQL
  2. Соединение таблиц
  3. Подзапросы

Текст задания

Составить запросы на языке SQL (пункты 1-7).

  1. Сделать запрос для получения атрибутов из указанных таблиц, применив фильтры по указанным условиям: Таблицы: НОЦЕНКИ, НВЕДОМОСТИ. Вывести атрибуты: НОЦЕНКИ.ПРИМЕЧАНИЕ, НВЕДОМОСТИ.ДАТА. Фильтры (AND): a) НОЦЕНКИ.КОД = 5. b) НВЕДОМОСТИ.ДАТА = 2022-06-08. c) Н_ВЕДОМОСТИ.ДАТА > 2010-06-18. Вид соединения: RIGHT JOIN.

  2. Сделать запрос для получения атрибутов из указанных таблиц, применив фильтры по указанным условиям: Таблицы: НЛЮДИ, НОБУЧЕНИЯ, НУЧЕНИКИ. Вывести атрибуты: НЛЮДИ.ОТЧЕСТВО, НОБУЧЕНИЯ.НЗК, НУЧЕНИКИ.ИД. Фильтры: (AND) a) НЛЮДИ.ИД = 152862. b) НОБУЧЕНИЯ.НЗК > 999080. Вид соединения: INNER JOIN.

  3. Вывести число студентов вечерней формы обучения, которые без ИНН. Ответ должен содержать только одно число.

  4. В таблице НГРУППЫПЛАНОВ найти номера планов, по которым обучается (обучалось) менее 2 групп на кафедре вычислительной техники.Для реализации использовать соединение таблиц.

  5. Выведите таблицу со средним возрастом студентов во всех группах (Группа, Средний возраст), где средний возраст больше минимального возраста в группе 1100.

  6. Получить список студентов, отчисленных после первого сентября 2012 года с заочной формы обучения (специальность: 230101). В результат включить: номер группы; номер, фамилию, имя и отчество студента; номер пункта приказа. Для реализации использовать соединение таблиц.

  7. Вывести список студентов, имеющих одинаковые фамилии, но не совпадающие даты рождения.

Выполнение работы

Запрос 1

Для начала, выполним запрос, касающийся только отношения Н_ВЕДОМОСТИ:

SELECT Н_ВЕДОМОСТИ.ДАТА, Н_ВЕДОМОСТИ.ОЦЕНКА
FROM Н_ВЕДОМОСТИ
WHERE Н_ВЕДОМОСТИ.ДАТА = '2022-06-08'
AND Н_ВЕДОМОСТИ.ДАТА > '2010-06-18';

Результат (таблица 1):

ДАТА ОЦЕНКА
2022-06-08 00:00:00 неявка
2022-06-08 00:00:00 зачет
2022-06-08 00:00:00 неявка

Далее, выполним запрос, касающийся только отношения Н_ОЦЕНКИ:

SELECT Н_ОЦЕНКИ.ПРИМЕЧАНИЕ, Н_ОЦЕНКИ.КОД
FROM Н_ОЦЕНКИ
WHERE Н_ОЦЕНКИ.КОД = '5';

Результат (таблица 2):

ПРИМЕЧАНИЕ КОД
отлично 5

Заметим, что атрибут Н_ОЦЕНКИ.КОД может быть использован для объединения отношений Н_ОЦЕНКИ и Н_ВЕДОМОСТИ. Но результат такой операции нам даст пустое отношение:

Запрос:

SELECT Н_ОЦЕНКИ.ПРИМЕЧАНИЕ, Н_ВЕДОМОСТИ.ДАТА
FROM Н_ОЦЕНКИ
RIGHT JOIN Н_ВЕДОМОСТИ
  ON Н_ВЕДОМОСТИ.ОЦЕНКА = Н_ОЦЕНКИ.КОД
WHERE Н_ВЕДОМОСТИ.ДАТА = '2022-06-08'
  AND Н_ВЕДОМОСТИ.ДАТА > '2010-06-18'
  AND Н_ОЦЕНКИ.КОД = '5';

Даст нам пустое отношение.

Запрос 2

Выполним запрос касаемо отношения Н_ЛЮДИ:

SELECT Н_ЛЮДИ.ИД, Н_ЛЮДИ.ОТЧЕСТВО
FROM Н_ЛЮДИ
WHERE Н_ЛЮДИ.ИД = 152862;

Результат:

"ИД" "ОТЧЕСТВО"
152862 "Юльевна"

Как видим, случай как и в задании 1, получился тоже довольно вырожденный. Результат всего один кортеж.

Далее, выполним запрос, касаемо отношения Н_ОБУЧЕНИЯ:

SELECT Н_ОБУЧЕНИЯ.НЗК
FROM Н_ОБУЧЕНИЯ
WHERE Н_ОБУЧЕНИЯ.НЗК > '999080';

Результат пустой.

Для того, чтобы объединить отношения Н_ОБУЧЕНИЯ и Н_ЛЮДИ, заметим, что атрибуты по которым мы их можем связать следующие: Н_ОБУЧЕНИЯ.ЧЛВК_ИД и Н_ЛЮДИ.ИД.

SELECT *
FROM Н_ЛЮДИ
INNER JOIN Н_ОБУЧЕНИЯ ON Н_ОБУЧЕНИЯ.ЧЛВК_ИД = Н_ЛЮДИ.ИД;

Навесим еще отношение Н_УЧЕНИКИ ко всему выше через атрибут Н_УЧЕНИКИ.ИД:

SELECT *
FROM Н_ЛЮДИ
INNER JOIN Н_ОБУЧЕНИЯ ON Н_ОБУЧЕНИЯ.ЧЛВК_ИД = Н_ЛЮДИ.ИД
INNER JOIN Н_УЧЕНИКИ ON Н_УЧЕНИКИ.ИД = Н_ЛЮДИ.ИД;

Теперь выведем только требуемые атрибуты и добавим фильтры, предсказуемо получим пустое отношение, запрос:

SELECT Н_ЛЮДИ.ОТЧЕСТВО, Н_ОБУЧЕНИЯ.НЗК, Н_УЧЕНИКИ.ИД
FROM Н_ЛЮДИ
INNER JOIN Н_ОБУЧЕНИЯ
	ON Н_ОБУЧЕНИЯ.ЧЛВК_ИД = Н_ЛЮДИ.ИД
INNER JOIN Н_УЧЕНИКИ
	ON Н_УЧЕНИКИ.ИД = Н_ЛЮДИ.ИД
WHERE Н_ЛЮДИ.ИД = 152862
	AND Н_ОБУЧЕНИЯ.НЗК > '999080';

Даст нам пустое отношение.

Запрос 3

Для того, чтобы сопоставить ученика с формой обучения, воспользуемся отношением Н_ПЛАНЫ, через него мы можем получить форму обучения, т.к. за планом закреплена форма обучения через Н_ПЛАНЫ.ФО_ИД, а за каждым студентом закреплен план Н_УЧЕНИКИ.ПЛАН_ИД

Выполнив запрос ниже, я выяснил, что ни у кого из людей нет инн, тем не менее, по заданию я учту это в условии:

SELECT Н_ЛЮДИ.ФАМИЛИЯ, Н_ЛЮДИ.ИМЯ, Н_ЛЮДИ.ОТЧЕСТВО, Н_ЛЮДИ.ИНН ,Н_ФОРМЫ_ОБУЧЕНИЯ.НАИМЕНОВАНИЕ
FROM Н_УЧЕНИКИ
JOIN Н_ЛЮДИ ON Н_УЧЕНИКИ.ЧЛВК_ИД=Н_ЛЮДИ.ИД
JOIN Н_ПЛАНЫ ON Н_УЧЕНИКИ.ПЛАН_ИД = Н_ПЛАНЫ.ПЛАН_ИД
JOIN Н_ФОРМЫ_ОБУЧЕНИЯ ON Н_ПЛАНЫ.ФО_ИД = Н_ФОРМЫ_ОБУЧЕНИЯ.ИД;

Результат (первые 10 строк):

ФАМИЛИЯ ИМЯ ОТЧЕСТВО ИНН НАИМЕНОВАНИЕ
Захаров Антон Павлович "NULL" Очная
Краснояров Станислав Евгеньевич "NULL" Очная
Воеводкина Елена Сергеевна "NULL" Очная
Кузнецов Александр Александрович "NULL" Очная
Кучук Максим Григорьевич "NULL" Очная
Новосельский Вениамин Борисович "NULL" Очная
Чугунова Татьяна Михайловна "NULL" Очная
Чумаков Андрей Евгеньевич "NULL" Очная
Улановский Михаил Александрович "NULL" Очная
Гуркин Дмитрий Ильич "NULL" Очная
Галушко Илья Сергеевич "NULL" Очная

Теперь просто добавим условие для ИНН, формы обучения и посчитаем:

SELECT COUNT(*)
FROM Н_УЧЕНИКИ
JOIN Н_ЛЮДИ
	ON Н_УЧЕНИКИ.ЧЛВК_ИД=Н_ЛЮДИ.ИД
JOIN Н_ПЛАНЫ
	ON Н_УЧЕНИКИ.ПЛАН_ИД = Н_ПЛАНЫ.ПЛАН_ИД
JOIN Н_ФОРМЫ_ОБУЧЕНИЯ
	ON Н_ПЛАНЫ.ФО_ИД = Н_ФОРМЫ_ОБУЧЕНИЯ.ИД
WHERE Н_ФОРМЫ_ОБУЧЕНИЯ.ИД = 2
	AND Н_ЛЮДИ.ИНН IS NULL;

Результат: 834

Запрос 4

Выполним запрос, который выведет нам все группы на кафедре ВТ

SELECT Н_ГРУППЫ_ПЛАНОВ.ГРУППА, Н_ГРУППЫ_ПЛАНОВ.ПЛАН_ИД
FROM Н_ГРУППЫ_ПЛАНОВ
JOIN Н_ПЛАНЫ ON Н_ГРУППЫ_ПЛАНОВ.ПЛАН_ИД=Н_ПЛАНЫ.ПЛАН_ИД
JOIN Н_ОТДЕЛЫ ON Н_ОТДЕЛЫ.ИД=Н_ПЛАНЫ.ОТД_ИД_ЗАКРЕПЛЕН_ЗА
WHERE Н_ОТДЕЛЫ.КОРОТКОЕ_ИМЯ = 'ВТ';

Результат (первые 10 строк):

"ГРУППА" "ПЛАН_ИД"
"5111" 5621
"5113" 6147
"5103" 5610
"5114" 6150
"5101" 5612
"5111" 4698
"5113" 5548
"5103" 4695
"5102" 4694
"5114" 5549

Теперь добавим условие, которое посчитает для каждого плана число его использований:

SELECT Н_ГРУППЫ_ПЛАНОВ.ПЛАН_ИД, COUNT(*)
FROM Н_ГРУППЫ_ПЛАНОВ
JOIN Н_ПЛАНЫ ON Н_ГРУППЫ_ПЛАНОВ.ПЛАН_ИД=Н_ПЛАНЫ.ПЛАН_ИД
JOIN Н_ОТДЕЛЫ ON Н_ОТДЕЛЫ.ИД=Н_ПЛАНЫ.ОТД_ИД_ЗАКРЕПЛЕН_ЗА
WHERE Н_ОТДЕЛЫ.КОРОТКОЕ_ИМЯ = 'ВТ'
GROUP BY Н_ГРУППЫ_ПЛАНОВ.ПЛАН_ИД;

Результат (первые 5 строк): "ПЛАН_ИД"|"count" 4542|1 272|4 773|2 304|1 3721|1

Теперь выведем только те, которые встречаются 1 раз:

SELECT Н_ГРУППЫ_ПЛАНОВ.ПЛАН_ИД
FROM Н_ГРУППЫ_ПЛАНОВ
JOIN Н_ПЛАНЫ ON Н_ГРУППЫ_ПЛАНОВ.ПЛАН_ИД=Н_ПЛАНЫ.ПЛАН_ИД
JOIN Н_ОТДЕЛЫ ON Н_ОТДЕЛЫ.ИД=Н_ПЛАНЫ.ОТД_ИД_ЗАКРЕПЛЕН_ЗА
WHERE Н_ОТДЕЛЫ.КОРОТКОЕ_ИМЯ = 'ВТ'
GROUP BY Н_ГРУППЫ_ПЛАНОВ.ПЛАН_ИД
HAVING COUNT(*) = 1;

Запрос 5

Выполним запрос, чтобы сопоставить человека, группу и данные о возрасте:

SELECT Н_УЧЕНИКИ.ИД, Н_УЧЕНИКИ.ГРУППА, Н_ЛЮДИ.ИМЯ, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ, Н_ЛЮДИ.ДАТА_СМЕРТИ
FROM Н_УЧЕНИКИ
JOIN Н_ЛЮДИ ON Н_УЧЕНИКИ.ИД=Н_ЛЮДИ.ИД
LIMIT 10;

Результат:

"ИД" "ГРУППА" "ИМЯ" "ДАТА_РОЖДЕНИЯ" "ДАТА_СМЕРТИ"
110139 "1100" "Алексей" "2011-11-11 00:00:00" "9999-09-09 00:00:00"
110141 "1100" "Руслан" "2011-11-11 00:00:00" "9999-09-09 00:00:00"
116494 "2539" "Василий" "1979-03-13 00:00:00" "9999-09-09 00:00:00"
111721 "1508" "Сергей" "2011-11-11 00:00:00" "9999-09-09 00:00:00"
110145 "6539" "Игорь" "2011-11-11 00:00:00" "9999-09-09 00:00:00"
110146 "6539" "Светлана" "2011-11-11 00:00:00" "9999-09-09 00:00:00"
110149 "6539" "Любовь" "2011-11-11 00:00:00" "9999-09-09 00:00:00"
110152 "6539" "Татьяна" "2011-11-11 00:00:00" "9999-09-09 00:00:00"
110153 "6539" "Светлана" "2011-11-11 00:00:00" "9999-09-09 00:00:00"
110154 "6539" "Александра" "1973-10-22 00:00:00" "9999-09-09 00:00:00"

Давайте посчитаем для каждого возраст:

SELECT
	Н_УЧЕНИКИ.ИД,
	Н_УЧЕНИКИ.ГРУППА,
	Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ,
	Н_ЛЮДИ.ДАТА_СМЕРТИ,
	CASE WHEN Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ > current_timestamp
		THEN NULL
		ELSE AGE(current_timestamp, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ)
	END
	AS возраст
FROM Н_УЧЕНИКИ
JOIN Н_ЛЮДИ ON Н_УЧЕНИКИ.ИД=Н_ЛЮДИ.ИД;

Результат:

"ИД" "ГРУППА" "ДАТА_РОЖДЕНИЯ" "ДАТА_СМЕРТИ" "возраст"
110139 "1100" "2011-11-11 00:00:00" "9999-09-09 00:00:00" "11 years 2 mons 12 days 18:32:05.096208"
110141 "1100" "2011-11-11 00:00:00" "9999-09-09 00:00:00" "11 years 2 mons 12 days 18:32:05.096208"
116494 "2539" "1979-03-13 00:00:00" "9999-09-09 00:00:00" "43 years 10 mons 10 days 18:32:05.096208"
111721 "1508" "2011-11-11 00:00:00" "9999-09-09 00:00:00" "11 years 2 mons 12 days 18:32:05.096208"
110145 "6539" "2011-11-11 00:00:00" "9999-09-09 00:00:00" "11 years 2 mons 12 days 18:32:05.096208"
110146 "6539" "2011-11-11 00:00:00" "9999-09-09 00:00:00" "11 years 2 mons 12 days 18:32:05.096208"
110149 "6539" "2011-11-11 00:00:00" "9999-09-09 00:00:00" "11 years 2 mons 12 days 18:32:05.096208"
110152 "6539" "2011-11-11 00:00:00" "9999-09-09 00:00:00" "11 years 2 mons 12 days 18:32:05.096208"
110153 "6539" "2011-11-11 00:00:00" "9999-09-09 00:00:00" "11 years 2 mons 12 days 18:32:05.096208"
110154 "6539" "1973-10-22 00:00:00" "9999-09-09 00:00:00" "49 years 3 mons 1 day 18:32:05.096208"

Теперь напишем запрос, который явно получает возраст, минимальный для группы 1100:

SELECT
	MIN(AGE(current_timestamp, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ))
FROM Н_УЧЕНИКИ
JOIN Н_ЛЮДИ ON Н_УЧЕНИКИ.ИД=Н_ЛЮДИ.ИД
WHERE AGE(current_timestamp, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ) > '0 days'
AND Н_УЧЕНИКИ.ГРУППА = '1100';

Результат:

min
11 years 2 mons 12 days 18:34:44.95577

Соберем из двух запросов выше один, для того чтобы вывести только те записи, которые удовлетворяют условию с помощью HAVING:

SELECT
	Н_УЧЕНИКИ.ГРУППА,
	AVG(AGE(current_timestamp, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ)) AS "Средний возраст"
FROM Н_УЧЕНИКИ
JOIN Н_ЛЮДИ ON Н_УЧЕНИКИ.ИД=Н_ЛЮДИ.ИД
WHERE AGE(current_timestamp, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ) > '0 days'
GROUP BY Н_УЧЕНИКИ.ГРУППА
HAVING AVG(AGE(current_timestamp, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ)) >
(
SELECT
	MIN(AGE(current_timestamp, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ))
FROM Н_УЧЕНИКИ
JOIN Н_ЛЮДИ ON Н_УЧЕНИКИ.ИД=Н_ЛЮДИ.ИД
WHERE AGE(current_timestamp, Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ) > '0 days'
AND Н_УЧЕНИКИ.ГРУППА = '1100'
);

Результат (первые 10 строк):

"ГРУППА" "Средний возраст"
"2508" "21 years 10 mons 7 days 30:37:44.718433"
"3102" "41 years 1 mon 29 days 37:49:44.718433"
"1105" "33 years 10 mons 25 days 35:57:44.747233"
"4104" "35 years 4 mons 31 days 24:37:44.718433"
"6112" "42 years 18 days 18:37:44.718433"
"5110" "32 years 9 mons 10 days 18:37:44.718433"
"4100" "40 years 10 mons 5 days 18:37:44.718433"
"4106" "29 years 8 mons 23 days 38:37:44.718433"
"6110" "36 years 4 mons 38 days 34:37:44.718433"
"1539" "33 years 1 day 18:37:44.718433"

Запрос 6

Разделим задание на пункты:

  1. Получить список студентов
    1. отчисленных после первого сентября 2012 года
    2. с заочной формы обучения
    3. (специальность: 230101)
  2. В результат включить:
    1. номер группы
    2. номер студента
    3. фамилию студента
    4. имя студента
    5. отчество студента
    6. номер пункта приказа

Каждому пункту выше соответствует атрибут:

  1. Получить список студентов
    1. отчисленных после первого сентября 2012 года — Н_УЧЕНИКИ.НАЧАЛО
    2. с заочной формы обучения — НПЛАНЫ.ФОИД
    3. (специальность: 230101) – ННАПРСПЕЦ.КОД_НАПРСПЕЦ
  2. В результат включить:
    1. номер группы — Н_УЧЕНИКИ.ГРУППА
    2. номер студента — НУЧЕНИКИ.ЧЛВКИД
    3. фамилию студента — Н_ЛЮДИ.ФАМИЛИЯ
    4. имя студента — Н_ЛЮДИ.ИМЯ
    5. отчество студента — Н_ЛЮДИ.ОТЧЕСТВО
    6. номер пункта приказа — НУЧЕНИКИ.ППРКОКИД (В комментарии указанно, что это лишь внешний ключ к НПУНКТЫПРИКАЗОВОК, но отношение с таким именем мне найти не удалось)

Запрос:

SELECT
	Н_УЧЕНИКИ.ГРУППА AS "Номер группы",
	Н_УЧЕНИКИ.ЧЛВК_ИД AS "Номер студента",
	Н_ЛЮДИ.ФАМИЛИЯ,
	Н_ЛЮДИ.ИМЯ,
	Н_ЛЮДИ.ОТЧЕСТВО,
	Н_УЧЕНИКИ.П_ПРКОК_ИД AS "ИД пункта приказа"
FROM Н_УЧЕНИКИ
JOIN Н_ПЛАНЫ
	ON Н_УЧЕНИКИ.ПЛАН_ИД = Н_ПЛАНЫ.ПЛАН_ИД
JOIN Н_НАПР_СПЕЦ
	ON Н_ПЛАНЫ.НАПС_ИД = Н_НАПР_СПЕЦ.ИД
JOIN Н_ЛЮДИ
	ON Н_УЧЕНИКИ.ЧЛВК_ИД = Н_ЛЮДИ.ИД
WHERE Н_УЧЕНИКИ.ПРИЗНАК = 'отчисл'
	AND Н_УЧЕНИКИ.СОСТОЯНИЕ = 'утвержден'
	AND Н_УЧЕНИКИ.НАЧАЛО > '2022-09-01'
	AND Н_НАПР_СПЕЦ.КОД_НАПРСПЕЦ = '230101'
	AND Н_ПЛАНЫ.ФО_ИД = 3;

Результат пуст.

Запрос 7

Очень странно, что нельзя создавать представления. Пришлось использовать подзапросы.

Ход мыслей:

-- запрос 1
-- получим всех учеников
SELECT
	Н_ЛЮДИ.ИД,
	Н_ЛЮДИ.ФАМИЛИЯ,
	Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
FROM
	Н_ЛЮДИ
WHERE
	Н_ЛЮДИ.ИД IN (
		SELECT
			Н_УЧЕНИКИ.ЧЛВК_ИД
		FROM
			Н_УЧЕНИКИ);

-- запрос 2
-- получим все даты рождения, которые встречаются более одного раза
SELECT
	q1.ДАТА_РОЖДЕНИЯ,
	count(q1.ДАТА_РОЖДЕНИЯ)
FROM (
	SELECT
		Н_ЛЮДИ.ИД,
		Н_ЛЮДИ.ФАМИЛИЯ,
		Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
	FROM
		Н_ЛЮДИ
	WHERE
		Н_ЛЮДИ.ИД IN (
			SELECT
				Н_УЧЕНИКИ.ЧЛВК_ИД
			FROM
				Н_УЧЕНИКИ)) AS q1
GROUP BY
	q1.ДАТА_РОЖДЕНИЯ
HAVING
	count(q1.ДАТА_РОЖДЕНИЯ) > 1;

-- запрос 3
-- получим всех учеников, дата рождения которых встречается более одного раза
SELECT
	*
FROM (
	SELECT
		Н_ЛЮДИ.ИД,
		Н_ЛЮДИ.ФАМИЛИЯ,
		Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
	FROM
		Н_ЛЮДИ
	WHERE
		Н_ЛЮДИ.ИД IN (
			SELECT
				Н_УЧЕНИКИ.ЧЛВК_ИД
			FROM
				Н_УЧЕНИКИ)) AS all_students
WHERE
	all_students.ДАТА_РОЖДЕНИЯ IN (
		SELECT
			q1.ДАТА_РОЖДЕНИЯ
		FROM (
			SELECT
				Н_ЛЮДИ.ИД,
				Н_ЛЮДИ.ФАМИЛИЯ,
				Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
			FROM
				Н_ЛЮДИ
			WHERE
				Н_ЛЮДИ.ИД IN (
					SELECT
						Н_УЧЕНИКИ.ЧЛВК_ИД
					FROM
						Н_УЧЕНИКИ)) AS q1
			GROUP BY
				q1.ДАТА_РОЖДЕНИЯ
			HAVING
				count(q1.ДАТА_РОЖДЕНИЯ) > 1);

-- запрос 4
-- теперь нужно убрать всех студентов из предыдущего запроса, чья фамилия встречается только 1 раз, сначала найдем такие фамилии
SELECT
	ФАМИЛИЯ,
	count(*)
FROM (
	SELECT
		ФАМИЛИЯ
	FROM (
		SELECT
			Н_ЛЮДИ.ИД,
			Н_ЛЮДИ.ФАМИЛИЯ,
			Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
		FROM
			Н_ЛЮДИ
		WHERE
			Н_ЛЮДИ.ИД IN (
				SELECT
					Н_УЧЕНИКИ.ЧЛВК_ИД
				FROM
					Н_УЧЕНИКИ)) AS all_students
		WHERE
			all_students.ДАТА_РОЖДЕНИЯ IN (
				SELECT
					q1.ДАТА_РОЖДЕНИЯ
				FROM (
					SELECT
						Н_ЛЮДИ.ИД,
						Н_ЛЮДИ.ФАМИЛИЯ,
						Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
					FROM
						Н_ЛЮДИ
					WHERE
						Н_ЛЮДИ.ИД IN (
							SELECT
								Н_УЧЕНИКИ.ЧЛВК_ИД
							FROM
								Н_УЧЕНИКИ)) AS q1
					GROUP BY
						q1.ДАТА_РОЖДЕНИЯ
					HAVING
						count(q1.ДАТА_РОЖДЕНИЯ) > 1)) AS birthday_more_than_once
GROUP BY
	ФАМИЛИЯ
HAVING
	count(ФАМИЛИЯ) = 1;

-- запрос 5
-- исключим студентов с фамилиями, полученными в запросе 4 из запроса 3, это и будет ответ на вопрос задачи

SELECT
	*
FROM (
	SELECT
		*
	FROM (
		SELECT
			Н_ЛЮДИ.ИД,
			Н_ЛЮДИ.ФАМИЛИЯ,
			Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
		FROM
			Н_ЛЮДИ
		WHERE
			Н_ЛЮДИ.ИД IN (
				SELECT
					Н_УЧЕНИКИ.ЧЛВК_ИД
				FROM
					Н_УЧЕНИКИ)) AS all_students
		WHERE
			all_students.ДАТА_РОЖДЕНИЯ IN (
				SELECT
					q1.ДАТА_РОЖДЕНИЯ
				FROM (
					SELECT
						Н_ЛЮДИ.ИД,
						Н_ЛЮДИ.ФАМИЛИЯ,
						Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
					FROM
						Н_ЛЮДИ
					WHERE
						Н_ЛЮДИ.ИД IN (
							SELECT
								Н_УЧЕНИКИ.ЧЛВК_ИД
							FROM
								Н_УЧЕНИКИ)) AS q1
					GROUP BY
						q1.ДАТА_РОЖДЕНИЯ
					HAVING
						count(q1.ДАТА_РОЖДЕНИЯ) > 1)) AS query3
WHERE
	query3.ФАМИЛИЯ NOT IN (
		SELECT
			ФАМИЛИЯ
		FROM (
			SELECT
				ФАМИЛИЯ
			FROM (
				SELECT
					Н_ЛЮДИ.ИД,
					Н_ЛЮДИ.ФАМИЛИЯ,
					Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
				FROM
					Н_ЛЮДИ
				WHERE
					Н_ЛЮДИ.ИД IN (
						SELECT
							Н_УЧЕНИКИ.ЧЛВК_ИД
						FROM
							Н_УЧЕНИКИ)) AS all_students
				WHERE
					all_students.ДАТА_РОЖДЕНИЯ IN (
						SELECT
							q1.ДАТА_РОЖДЕНИЯ
						FROM (
							SELECT
								Н_ЛЮДИ.ИД,
								Н_ЛЮДИ.ФАМИЛИЯ,
								Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
							FROM
								Н_ЛЮДИ
							WHERE
								Н_ЛЮДИ.ИД IN (
									SELECT
										Н_УЧЕНИКИ.ЧЛВК_ИД
									FROM
										Н_УЧЕНИКИ)) AS q1
							GROUP BY
								q1.ДАТА_РОЖДЕНИЯ
							HAVING
								count(q1.ДАТА_РОЖДЕНИЯ) > 1)) AS birthday_more_than_once
					GROUP BY
						ФАМИЛИЯ
					HAVING
						count(ФАМИЛИЯ) = 1)
			ORDER BY
				ФАМИЛИЯ;

Запрос:

SELECT
	*
FROM (
	SELECT
		*
	FROM (
		SELECT
			Н_ЛЮДИ.ИД,
			Н_ЛЮДИ.ФАМИЛИЯ,
			Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
		FROM
			Н_ЛЮДИ
		WHERE
			Н_ЛЮДИ.ИД IN (
				SELECT
					Н_УЧЕНИКИ.ЧЛВК_ИД
				FROM
					Н_УЧЕНИКИ)) AS all_students
		WHERE
			all_students.ДАТА_РОЖДЕНИЯ IN (
				SELECT
					q1.ДАТА_РОЖДЕНИЯ
				FROM (
					SELECT
						Н_ЛЮДИ.ИД,
						Н_ЛЮДИ.ФАМИЛИЯ,
						Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
					FROM
						Н_ЛЮДИ
					WHERE
						Н_ЛЮДИ.ИД IN (
							SELECT
								Н_УЧЕНИКИ.ЧЛВК_ИД
							FROM
								Н_УЧЕНИКИ)) AS q1
					GROUP BY
						q1.ДАТА_РОЖДЕНИЯ
					HAVING
						count(q1.ДАТА_РОЖДЕНИЯ) > 1)) AS query3
WHERE
	query3.ФАМИЛИЯ NOT IN (
		SELECT
			ФАМИЛИЯ
		FROM (
			SELECT
				ФАМИЛИЯ
			FROM (
				SELECT
					Н_ЛЮДИ.ИД,
					Н_ЛЮДИ.ФАМИЛИЯ,
					Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
				FROM
					Н_ЛЮДИ
				WHERE
					Н_ЛЮДИ.ИД IN (
						SELECT
							Н_УЧЕНИКИ.ЧЛВК_ИД
						FROM
							Н_УЧЕНИКИ)) AS all_students
				WHERE
					all_students.ДАТА_РОЖДЕНИЯ IN (
						SELECT
							q1.ДАТА_РОЖДЕНИЯ
						FROM (
							SELECT
								Н_ЛЮДИ.ИД,
								Н_ЛЮДИ.ФАМИЛИЯ,
								Н_ЛЮДИ.ДАТА_РОЖДЕНИЯ
							FROM
								Н_ЛЮДИ
							WHERE
								Н_ЛЮДИ.ИД IN (
									SELECT
										Н_УЧЕНИКИ.ЧЛВК_ИД
									FROM
										Н_УЧЕНИКИ)) AS q1
							GROUP BY
								q1.ДАТА_РОЖДЕНИЯ
							HAVING
								count(q1.ДАТА_РОЖДЕНИЯ) > 1)) AS birthday_more_than_once
					GROUP BY
						ФАМИЛИЯ
					HAVING
						count(ФАМИЛИЯ) = 1)
			ORDER BY
				ФАМИЛИЯ;

Результат (первые 10 строк):

"ИД" "ФАМИЛИЯ" "ДАТА_РОЖДЕНИЯ"
153319 "Абрамов" "1992-03-25 00:00:00"
113854 "Абрамов" "2011-11-11 00:00:00"
142342 "Абрамов" "1989-01-22 00:00:00"
116670 "Авдеев" "2011-11-11 00:00:00"
153320 "Авдеев" "1992-01-18 00:00:00"
145409 "Агапова" "1990-12-13 00:00:00"
137740 "Агапова" "1988-05-18 00:00:00"
111665 "Аксенов" "1977-09-17 00:00:00"
120184 "Аксенов" "1983-05-23 00:00:00"
110595 "Аксенов" "2011-11-11 00:00:00"

Вывод

В ходе работы я изучил как можно использовать запросы SQL для выборки необходимых данных из схемы. В работе я использовал соединения таблиц, подзапросы, условия. Также использовал даталогическую модель и комментарии к атрибутам представленные в схеме public БД ucheb.