ЕГЭ · Информатика · Базы данных и таблицы
Реляционные базы данных
Понять таблицу, поле, запись, ключ — язык задания 3.
🎯 ЕГЭ информатика: задания 3, 9, 18. №3 — базы данных; №9 — обработка числовой информации в электронных таблицах; №18 — динамическое программирование: Робот на поле.
- ⚠Считают по одной таблице, забывая связь по ключу со второй
- ⚠Путают ID и IDродителя при поиске предков/потомков в родословных
| Запись | строка таблицы — сведения об одном объекте |
|---|---|
| Поле | столбец — одна характеристика |
| Ключ | поле (или набор), однозначно определяющее запись |
| Операции | поиск, фильтр (по условию), сортировка |
Ключ обязан быть уникальным и не пустым. Если поле может повторяться (например, Наименование товара), ключом оно быть не может.
Почему таблиц три, а не одна
Задание 3 всегда показывает базу из нескольких таблиц, и первый вопрос ученика — зачем их разделили. Ответ: чтобы не хранить одно и то же дважды.
Представьте единую таблицу операций, где рядом с каждой продажей записаны и адрес магазина, и район, и полное наименование товара, и цена. Магазин на улице Лесной встречается в базе восемьсот раз — значит, его адрес и район записаны восемьсот раз. Это плохо по трём причинам.
Первая — объём. Восемьсот повторов строки «Центральный, ул. Лесная, 3» занимают в сотни раз больше места, чем одна такая запись плюс восемьсот чисел-ссылок.
Вторая — противоречия. Если магазин переехал, адрес нужно поменять в восьмистах местах. Пропустили одно — и база утверждает, что один и тот же магазин находится сразу по двум адресам. Такое состояние называют аномалией обновления, и именно ради защиты от него данные разносят по таблицам.
Третья — потеря сведений. Пока в базе нет ни одной операции с новым магазином, в единой таблице его адрес просто негде записать: строка появляется только вместе с продажей. Отдельная таблица «Магазин» хранит магазин независимо от того, торговал он или нет.
Поэтому базу разбивают так: каждая сущность (магазин, товар, операция) живёт в своей таблице, а связь выражается ссылкой на ключ. В таблице операций вместо адреса стоит короткое «ID магазина = 1», и по этому числу адрес находится в таблице «Магазин» ровно в одном месте.
Поле, которое ссылается на ключ другой таблицы, называют внешним ключом. В базе задания 3 внешних ключей два: «ID магазина» и «Артикул» в таблице движения товаров. Ключ таблицы — «свой», внешний ключ — «чужой»: он не обязан быть уникальным, наоборот, он повторяется в стольких строках, сколько операций прошло через этот магазин.
Как устроен файл задания 3 и порядок работы
На экзамене к линии 3 приложен файл .xlsx с тремя листами, по листу на таблицу. Первая строка каждого листа — шапка с названиями полей, дальше данные. Никаких формул в файле нет: это чистые данные, всё остальное делаете вы.
Вопрос почти всегда одной формы: на какую сумму продан товар X в районе Y за период с A по B включительно. Стоимость одной продажи равна количеству упаковок, умноженному на цену за упаковку. Значит, ответ — сумма произведений по тем строкам движения товаров, которые проходят четыре фильтра сразу.
Вот эти четыре фильтра, и каждый из них кто-нибудь да забывает:
Тип операции — только «Продажа». В таблице лежат и поступления; они нужны для других формулировок, а здесь их надо отбросить. Забыть этот фильтр — самая частая ошибка линии 3, и ответ получается почти вдвое больше нужного.
Наименование товара — его нет в таблице операций, там только артикул. Значит, наименование берётся из таблицы «Товар» по артикулу. Причём разные артикулы могут иметь одинаковое наименование — например, один и тот же кефир разных поставщиков. Поэтому фильтровать нужно по наименованию, а не «найти артикул кефира» и искать по нему.
Район — его тоже нет в таблице операций, там только «ID магазина». Район берётся из таблицы «Магазин». В одном районе почти всегда несколько магазинов; отобрать один — ещё одна типичная потеря баллов.
Даты — «с 13 по 21 включительно» означает, что обе границы входят. Сравнения должны быть нестрогими: >= и <=. Строгое сравнение отрезает два дня, и ответ уезжает.
Порядок работы простой: сначала сведите справочники в удобный вид (в таблице — служебные столбцы, в Python — словари), потом пройдите по операциям и накопите сумму. Никакой сортировки и ручного поиска — строк в файле тысячи.
Разбор примера
Собираем строку из трёх таблиц вручную
Даны три маленькие таблицы. «Магазин»: 1 — Центральный, ул. Лесная; 2 — Кировский, пр. Мира; 3 — Центральный, ул. Садовая. «Товар»: 1001 — Кефир 1%, цена 78 руб. за упаковку; 1002 — Сметана 20%, цена 145 руб. «Движение товаров» — восемь операций. Определить, на какую сумму продан «Кефир 1%» в Центральном районе с 13 по 21 октября включительно.
Операции (дата, ID магазина, артикул, количество, тип):
- • | 12.10 | 1 | 1001 | 40 | Продажа |
- • | 13.10 | 1 | 1001 | 25 | Продажа |
- • | 14.10 | 2 | 1001 | 60 | Продажа |
- • | 15.10 | 1 | 1001 | 30 | Поступление |
- • | 18.10 | 3 | 1001 | 18 | Продажа |
- • | 21.10 | 1 | 1002 | 12 | Продажа |
- • | 21.10 | 3 | 1001 | 10 | Продажа |
- • | 22.10 | 1 | 1001 | 50 | Продажа |
Показать решение по шагам
- 1. Шаг 1. Разберём каждую операцию так, как это делает программа: подставим вместо ключей значения из справочников и проверим четыре условия. Сама операция не знает ни района, ни наименования — их надо подтянуть.
- 2. Шаг 2. Операция 12.10, магазин 1, артикул 1001, 40 шт., Продажа. Магазин 1 → Центральный ✔ Артикул 1001 → Кефир 1% ✔ Тип «Продажа» ✔ Но дата 12 октября раньше 13-го ✘ Строка отбрасывается. Сорок упаковок не в счёт.
- 3. Шаг 3. Операция 13.10, магазин 1, артикул 1001, 25 шт., Продажа. Район Центральный ✔ Товар Кефир 1% ✔ Тип «Продажа» ✔ Дата 13 октября — граница периода, и она входит ✔ Считаем: 25 × 78 = 1950 руб. Накопитель равен 1950.
- 4. Шаг 4. Операция 14.10, магазин 2, артикул 1001, 60 шт., Продажа. Товар и тип подходят, дата в периоде, но магазин 2 — это Кировский район ✘ Здесь и прячется ловушка: без справочника магазинов в самой операции стоит безобидное число 2, и строку легко засчитать по ошибке.
- 5. Шаг 5. Операция 15.10, магазин 1, артикул 1001, 30 шт., Поступление. Всё совпадает, кроме типа: это поступление товара в магазин, а не продажа ✘ Тридцать упаковок приехали на склад, денег покупатели не платили.
- 6. Шаг 6. Операция 18.10, магазин 3, артикул 1001, 18 шт., Продажа. Магазин 3 → Центральный ✔ Именно здесь второй раз спотыкаются: в Центральном районе два магазина, 1 и 3, и учитывать надо оба. Считаем: 18 × 78 = 1404 руб. Накопитель 1950 + 1404 = 3354.
- 7. Шаг 7. Операция 21.10, магазин 1, артикул 1002, 12 шт., Продажа. Район и дата подходят, но артикул 1002 → Сметана 20%, а нас просили про кефир ✘
- 8. Шаг 8. Операция 21.10, магазин 3, артикул 1001, 10 шт., Продажа. Всё сходится, дата 21 октября — вторая граница, тоже входит ✔ Считаем: 10 × 78 = 780 руб. Накопитель 3354 + 780 = 4134.
- 9. Шаг 9. Операция 22.10, магазин 1, артикул 1001, 50 шт., Продажа. Дата на день позже конца периода ✘ Пятьдесят упаковок — заметная сумма, и если перепутать «по 21-е» с «до 22-го», ответ вырастет на 3900 руб.
- 10. Шаг 10. Итог: 1950 + 1404 + 780 = 4134 руб. Из восьми операций подошли три, и каждая отброшенная строка не прошла по своей причине: дата раньше, чужой район, не тот тип, не тот товар, дата позже. Так устроены и боевые файлы, только строк там несколько тысяч.
Ответ: 4134 руб.
Разбор примера
Тот же ответ программой на Python
Написать программу, которая читает приложенный файл l3-b954af29a0.xlsx с тремя листами и выдаёт сумму продаж товара «Кефир 1%» в магазинах Центрального района с 13 по 21 октября включительно.
Показать решение по шагам
- 1.
Шаг 1. План повторяет ручной разбор: сначала прочитать два справочника в словари, потом один раз пройти по таблице операций. Вся программа:
```python
from openpyxl import load_workbook from datetime import datetimewb = loadworkbook('l3-b954af29a0.xlsx')
rayon_magazina = {} for id_mag, rayon, adres in wb['Магазин'].iter_rows(min_row=2, values_only=True): rayon_magazina[id_mag] = rayonopisanie_tovara = {} for artikul, otdel, nazvanie, ed, v_upakovke, cena in wb['Товар'].iter_rows(min_row=2, values_only=True): opisanie_tovara[artikul] = (nazvanie, cena)nachalo = datetime(2024, 10, 13) konec = datetime(2024, 10, 21)itog = 0 for nomer, data_op, id_mag, artikul, kolichestvo, tip in wb['Движение товаров'].iter_rows(min_row=2, values_only=True): if tip != 'Продажа': continuenazvanie, cena = opisanietovara[artikul]
if nazvanie != 'Кефир 1%': continue if rayon_magazina[id_mag] != 'Центральный': continue if not (nachalo <= data_op <= konec): continue itog += kolichestvo * cenaprint(itog) ```
- 2. Шаг 2. Зачем справочники кладут в словари, а не ищут по списку каждый раз? Потому что поиск в словаре по ключу занимает одинаковое время независимо от размера, а перебор списка — тем дольше, чем он длиннее. В файле восемь тысяч операций и, скажем, сорок магазинов; перебор дал бы триста двадцать тысяч сравнений вместо восьми тысяч мгновенных обращений. На линии 3 это не критично, но привычка правильная — в линии 27 она решает всё.
- 3. Шаг 3. Строка
for id_mag, rayon, adres in ...распаковывает кортеж строки сразу в три имени, по числу столбцов листа «Магазин». Если столбцов окажется больше, Python скажетtoo many values to unpack— сигнал, что вы не посмотрели шапку файла. Смотреть шапку перед написанием кода обязательно: порядок полей в разных вариантах может отличаться. - 4. Шаг 4. Параметр minrow=2 пропускает строку заголовков. Без него первой «операцией» окажется набор подписей «ID операции», «Дата», …, и на сравнении даты программа упадёт с ошибкой типов. Это ровно та ошибка, которую новички лечат случайными правками, хотя причина одна.
- 5. Шаг 5. Проследим выполнение на восьми демонстрационных операциях. Первая, 12.10, 40 шт.: тип «Продажа» — проходит; наименование «Кефир 1%» — проходит; район Центральный — проходит; дата 12 октября меньше
nachalo, условиеnachalo <= data_opложно →continue. Накопитель 0. - 6. Шаг 6. Вторая, 13.10, 25 шт.: все четыре проверки истинны. Двойное сравнение
nachalo <= data_op <= konecв Python работает как обычная математическая запись — это одно выражение, а не два. Накопитель: 0 + 25 × 78 = 1950. - 7. Шаг 7. Третья, 14.10, магазин 2, 60 шт.:
rayon_magazina[2]возвращает «Кировский», сравнение с «Центральный» ложно →continue. Накопитель 1950. Четвёртая, 15.10, Поступление: отсекается первой же проверкой, до справочников дело не доходит. Порядок проверок выбран так, чтобы самые дешёвые шли первыми. - 8. Шаг 8. Пятая, 18.10, магазин 3, 18 шт.:
rayon_magazina[3]— тоже «Центральный», строка засчитана. Накопитель: 1950 + 1404 = 3354. Шестая, 21.10, артикул 1002:opisanie_tovara[1002]даёт «Сметана 20%» →continue. - 9. Шаг 9. Седьмая, 21.10, магазин 3, 10 шт.: дата равна
konec, нестрогое сравнение пропускает её. Накопитель: 3354 + 780 = 4134. Восьмая, 22.10: дата большеkonec→continue. Цикл закончился,printвыводит 4134 — ровно то, что мы получили руками. - 10. Шаг 10. Две поломки, которые случаются именно с этим файлом. Первая:
TypeError: '<=' not supported between instances of 'str' and 'datetime'— значит, даты в листе хранятся текстом, а не датами. Лечится разбором строки:data_op = datetime.strptime(data_op, '%d.%m.%Y'). Вторая:KeyErrorна артикуле — в таблице операций встретился артикул, которого нет в справочнике товаров; в боевых файлах такого не бывает, значит, вы читаете не тот лист или забылиmin_row=2. - 11. Шаг 11. Ответ выводится целым числом, если цены целые. Когда в файле попадаются копейки, итог печатайте как
print(round(itog))или в том виде, который требует формулировка, — но округляйте только в конце, иначе набежит расхождение в несколько рублей и балл потеряется.
Ответ: 4134 (программа выводит ту же сумму, что получена вручную)
Разбор примера
Тот же ответ в таблице: ВПР и СУММЕСЛИМН
Решить ту же задачу линии 3 без программирования — средствами табличного процессора, служебными столбцами на листе «Движение товаров».
Показать решение по шагам
- 1. Шаг 1. В таблице нельзя «подтянуть» район прямо в условии — придётся сначала дописать недостающие поля служебными столбцами. Лист «Движение товаров» занимает столбцы A–F: A — ID операции, B — дата, C — ID магазина, D — артикул, E — количество упаковок, F — тип операции. Свободны G и дальше.
- 2.
Шаг 2. Столбец G — район магазина. В ячейку
G2пишем=ВПР(C2; Магазин!$A$2:$C$100; 2; ЛОЖЬ)и протягиваем вниз. Функция ВПР ищет значение
C2в первом столбце указанного диапазона и возвращает значение из столбца с номером 2 того же диапазона, то есть район. - 3. Шаг 3. Разберём аргументы, потому что здесь теряют больше всего времени. Диапазон закреплён долларами —
$A$2:$C$100: при протягивании он не должен съезжать, иначе нижние строки будут искать в обрезанном справочнике. А вотC2закреплять нельзя: каждая строка смотрит на свой магазин. Последний аргумент ЛОЖЬ означает «точное совпадение»; без него ВПР ищет приблизительно и на неотсортированном справочнике выдаёт чужие значения молча, без ошибки. - 4. Шаг 4. Столбец H — наименование товара:
=ВПР(D2; Товар!$A$2:$F$100; 3; ЛОЖЬ). Номер 3 — потому что на листе «Товар» наименование стоит третьим столбцом (артикул, отдел, наименование). Номер столбца считается внутри диапазона, а не по всему листу; если начать диапазон не с A, нумерация сдвинется, и это ещё одна классическая ошибка. - 5. Шаг 5. Столбец I — стоимость операции:
=E2 * ВПР(D2; Товар!$A$2:$F$100; 6; ЛОЖЬ). Шестой столбец справочника — цена за упаковку. Умножаем её на количество упаковок из E2. Поле «Количество в упаковке» (пятый столбец) в этой формулировке не участвует: цена дана именно за упаковку, а не за литр. Прочитать условие и понять, какая из двух величин нужна, — половина решения. - 6.
Шаг 6. Теперь всё нужное лежит в одной строке, и остаётся одна формула:
=СУММЕСЛИМН(I2:I1000; H2:H1000; "Кефир 1%"; G2:G1000; "Центральный"; F2:F1000; "Продажа"; B2:B1000; ">=" & ДАТА(2024;10;13); B2:B1000; "<=" & ДАТА(2024;10;21)) - 7. Шаг 7. Устройство СУММЕСЛИМН простое: первым идёт диапазон, который суммируем, дальше пары «диапазон условия — само условие». Пар может быть сколько угодно, и все они соединяются по И: строка попадает в сумму, только если выполнены все условия. Именно это нам и нужно — четыре фильтра сразу.
- 8. Шаг 8. Условия по датам записаны как текст со склейкой: знак сравнения в кавычках, амперсанд и функция ДАТА. Почему не написать
">=13.10.2024"целиком? Потому что распознавание даты внутри текста зависит от региональных настроек, и на чужом компьютере условие может не сработать. Функция ДАТА задаёт дату однозначно. - 9. Шаг 9. На нашей демонстрационной таблице из восьми строк формула даст 4134. Проверить себя легко: включите в служебном столбце фильтр по району «Центральный» и типу «Продажа» — останутся строки, которые мы разбирали руками, и их сумму покажет строка состояния внизу окна.
- 10. Шаг 10. Что выбрать на экзамене? Если вы уверенно пишете
ВПР, таблица быстрее: три формулы и протягивание занимают минуты три. Если формулы даются тяжело — Python надёжнее, потому что каждый фильтр там виден отдельной строкой и его легко проверить. Оба пути дают один и тот же балл; провальный путь только один — считать глазами.
Ответ: 4134 руб. — совпадает с ручным разбором и с программой
Разбор примера
Может ли поле быть ключом: проверяем кодом
В таблице «Товар» шесть записей: (1001, Кефир 1%, Молочный, 78), (1002, Сметана 20%, Молочный, 145), (1003, Творог 5%, Молочный, 96), (1004, Кефир 1%, Молочный, 82), (1005, Масло 82,5%, Молочный, 210), (1006, Творог 5%, Молочный, 96). Какие поля могут служить ключом таблицы?
Показать решение по шагам
- 1. Шаг 1. Ключ обязан однозначно определять запись: по его значению должна находиться ровно одна строка. Значит, проверка сводится к вопросу «есть ли в столбце повторы». Повторился хоть раз — ключом быть не может.
- 2. Шаг 2. Поле Артикул: 1001, 1002, 1003, 1004, 1005, 1006 — все значения различны ✔ Кандидат в ключи. Это и есть ключ таблицы «Товар» в задании 3, поэтому именно на артикул ссылается таблица операций.
- 3. Шаг 3. Поле Наименование товара: «Кефир 1%» встречается в записях 1001 и 1004, «Творог 5%» — в 1003 и 1006 ✘ Ключом быть не может. И это не выдумка ради примера: один и тот же продукт от разных поставщиков получает разные артикулы при одинаковом названии.
- 4. Шаг 4. Поле Отдел: во всех шести записях «Молочный» ✘ Столбец, где значение вообще одно на всю таблицу, бесполезен для различения записей — он несёт ноль информации о том, какая именно это строка.
- 5. Шаг 5. Поле Цена: 78, 145, 96, 82, 210, 96 — значение 96 повторилось ✘ И даже если бы все цены случайно оказались различными, ключом такое поле делать нельзя: завтра цены поменяются, и уникальность исчезнет. Ключ выбирают по смыслу, а не по удачному стечению данных.
- 6. Шаг 6. Пара полей тоже может быть ключом — такой называют составным. Пара (Наименование, Цена): («Кефир 1%», 78), («Сметана 20%», 145), («Творог 5%», 96), («Кефир 1%», 82), («Масло 82,5%», 210), («Творог 5%», 96). Последняя пара совпала с третьей ✘ А вот пара (Наименование, Артикул) уникальна — но она уникальна лишь потому, что уникален артикул, и второе поле в ней лишнее.
- 7.
Шаг 7. На реальном файле такие проверки делают программой. Вот она:
```python
from openpyxl import load_workbook from collections import Counterwb = load_workbook('l3-b954af29a0.xlsx') list_tovar = wb['Товар']shapka = [yacheyka.value for yacheyka in list_tovar[1]] stroki = list(list_tovar.iter_rows(min_row=2, values_only=True))for nomer, imya_polya in enumerate(shapka): znacheniya = [stroka[nomer] for stroka in stroki] schetchik = Counter(znacheniya) povtory = [z for z, k in schetchik.items() if k > 1] if povtory: print(imya_polya, '— не ключ, повторы:', povtory[:3]) else: print(imya_polya, '— может быть ключом')```
- 8. Шаг 8. Разберём устройство. Выражение
list_tovar[1]берёт первую строку листа — шапку, и списокshapkaхранит названия полей. Дальшеenumerate(shapka)выдаёт пары «номер столбца, название», аstroka[nomer]достаёт из каждой строки значение именно этого столбца. Получается перебор по столбцам, хотя данные лежат по строкам. - 9. Шаг 9. Класс Counter из модуля
collectionsсчитает, сколько раз встретилось каждое значение: это словарь «значение → количество». Списокpovtoryсобирает те значения, что встретились больше одного раза. Срез[:3]в выводе нужен, чтобы не залить экран тысячами повторов при проверке поля вроде «Отдел». - 10. Шаг 10. Проследим на наших шести записях. Столбец 0 — «Артикул»: Counter даёт по единице на каждое значение,
povtoryпуст → печатается «может быть ключом». Столбец 1 — «Наименование»: Counter выдаёт{'Кефир 1%': 2, 'Сметана 20%': 1, 'Творог 5%': 2, 'Масло 82,5%': 1}, повторы найдены → «не ключ». Столбец 2 — «Отдел»:{'Молочный': 6}→ «не ключ». Столбец 3 — «Цена»: повтор 96 → «не ключ». - 11. Шаг 11. Зачем это на экзамене, если в линии 3 ключи и так отмечены значком на схеме? Затем, что проверка уникальности — рабочий инструмент и в других линиях: так ищут дубликаты строк в файле, и так понимают, по какому полю таблицы соединяются, если схема нарисована неразборчиво. Один запуск даёт полную картину полей за секунду.
Ответ: ключом может быть только Артикул; наименование, отдел и цена повторяются
Вопрос на проверку
Что такое запись в реляционной таблице?
Ответить и проверить себя — после бесплатной регистрации.
Вопрос на проверку
Какое свойство обязательно для ключевого поля?
Ответить и проверить себя — после бесплатной регистрации.
Задание №3 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.
Задание №3 в формате экзамена
Ответить и проверить себя — после бесплатной регистрации.