Язык SQL. Формирование запросов к базе данных

  1. История развития SQL
  2. Структура SQL
  3. Типы данных SQL
  4. Оператор выбора SELECT
  5. Применение агрегатных функций и вложенных запросов в операторе выбора
  6. Вложенные запросы
  7. Внешние объединения
  8. Операторы манипулирования данными
  9. Задания для самостоятельной работы

Structured Query Language (Язык Структурированных Запросов) разработан корпораций IBM в начале 1970-х годов. В 1986 году SQL был впервые стандартизирован организаций ANSI.

SQL - этом мощный и в то же время не сложный язык для управления базами данных. Он поддерживается практически всеми современными базами данных. SQL подразделятся на два подмножества команд: DDL (Data Definition Language - язык определения данных) и DML (Data Manipulation Language - язык обработки данных). Команды DDL используются для создания новых баз данных, таблиц и столбцов, а команды DML - для чтения, записи, сортировки, фильтрования, удаления данных.

Здесь будут рассмотрены подробно лишь команды DML, поскольку их приходится использовать гораздо чаще, чем команды DDL, то есть дается просто понятие о SQL.

О командах DDL

CREATE - используется для создания новых таблиц, столбцов и индексов.

DROP - используется для удаления столбцов и индексов.

ALTER - используется для добавления в таблицы новых столбцов и изменения определенных столбцов.

Команды DML

SELECT - наиболее часто используемая команда, применяется для получения набора данных из таблицы базы данных. Команда SELECT имеет следующий синтаксис:

SELECT список_полей1 FROM имя_таблицы [WHERE критерий ORDER BY список_полей2 [ASC | DESC]]

Операторы, находящие внутри квадратных скобок не обязательны, а вертикальная черта означает, что должна присутствовать одна из указанных фраз, но не обе.

Для примера создадим простейший запрос на получение данных из полей "name" и "phone" таблицы "friends":

SELECT name, phone FROM friends

Если необходимо получить все поля таблицы, то не обязательно их перечислять, достаточно поставить звездочку (*):

SELECT * FROM friends

Для исключения из выводимого списка повторяющихся записей, используется ключевое слово DISTINCT:

SELECT DISTINCT name FROM friends

Если необходимо получить отдельную запись, то используется оператор WHERE. Например, нам надо получить из таблицы "friends" номер телефона "Сергей Иванов":

SELECT * FROM friends WHERE name = ' СергейИванов'

или наоборот, нам надо узнать кому принадлежит телефон 293-89-13:

SELECT * FROM friends WHERE phone = 293-89-13'

Помимо этого можно использовать подстановочные символы, таким образом, создавая шаблоны поиска. Для этого используется оператор LIKE. Оператор LIKE имеет следующие операторы подстановки:



* - соответствует строке состоящей из одного или более символов;

_ - соответствует одному любому символу;

[] - соответствует одному символу из определенного набора;

Например, для получения записей из поля "name" содержащих слово "Сергей", запрос будит выглядеть следующим образом:

SELECT * FROM friends WHERE name LIKE '*Сергей*'

Для определения порядка, в котором возвращаются данные, используется оператор ORDER BY. Без этого оператора порядок возвращаемых данных невозможно предсказать. Ключевые слова ASC и DESC позволяют определить направление сортировки. ASC - упорядочивает по возрастанию, а DESC - по убыванию.

Например, запрос на получение списка записей из поля "name" в алфавитном порядке будет выглядеть следующим образом:

SELECT * FROM friends ORDER BY name

Обратим внимание на то, что ключевое слово ASC указывать не обязательно, поскольку оно используется по умолчанию.

INSERT - данная команда служит для добавления новой записи в таблицу. Записывается она следующим образом:

INSERT INTO имя_таблицы VALUES (список_значений)

Обратим внимание на то, что типы значений в списке значений должны соответствовать типам значений полей таблицы, например:

INSERT INTO friends VALUES ('АннаОсипова', '495-09-81')

В данном примере в таблицу friends добавляется новая запись с указанными значениями.

UPDATE - эта команда применяется для обновления данных в таблице и чаще всего используется совместно с оператором WHERE. Команда UPDATE имеет следующий синтаксис:

UPDATE имя_таблицы SET имя_поля = значение [WHERE критерий]

Если опустить оператор WHERE, то будут обновлены данные во всех определенных полях таблицы. Для примера, поменяем номер телефона Сергея Иванова:

UPDATE friends SET phone = '255-55-55' WHERE name = 'Сергей Иванов'

DELETE - как вы уже наверное поняли, эта команда служит для удаления записей из таблицы. Как и UPDATE, команда DELETE обычно используется с оператором WHERE, если этот оператор пропустить, то будут удалены все данные из указанной таблицы. Синтаксис команды DELETE выглядит следующим образом:



DELETE FROM имя_таблицы [WHERE критерий]

Для примера, давайте удалим Сергея Иванова из нашей таблицы :) :

DELETE FROM friends WHERE name = 'СергейИванов'

Подробно об SQL

МАРТИН ГРУБЕР - Понимание SQL

Язык баз данных SQL со средствами поддержания целостности. Стандарт

к оглавлению

к оглавлению

История развития SQL

SQL (Structured Query Language) — Структурированный Язык Запросов — стандартный язык запросов по работе с реляционными БД. Язык SQL появился после реляционной алгебры, и его прототип был разработан в конце 70-х годов в компании IBM Research. Он был реализован в первом прототипе реляционной СУБД фирмы IBM System R. В дальнейшем этот язык применялся во многих коммерческих СУБД и в силу своего широкого распространения постепенно стал стандартом «де-факто» для языков манипулирования данными в реляционных СУБД.

Первый международный стандарт языка SQL был принят в 1989 г. (далее мы будем называть его SQL/89 или SQL1). Иногда стандарт SQL1 также называют стандартом ANSI/ISO, и подавляющее большинство доступных на рынке СУБД поддерживают этот стандарт полностью. Однако развитие информационных технологий, связанных с базами данных, и необходимость реализации переносимых приложений потребовали в скором времени доработки и расширения первого стандарта SQL.

В конце 1992 г. был принят новый международный стандарт языка SQL, который в дальнейшим будем называть SQL/92 или SQL2. И он не лишен недостатков, но в то же время является существенно более точным и полным, чем SQL/89. В настоящий момент большинство производителей СУБД внесли изменения в свои продукты так, чтобы они в большей степени удовлетворяли стандарту SQL2.

В 1999 году появился новый стандарт, названный SQL3. Если отличия между стандартами SQL1 и SQL2 во многом были количественными, то стандарт SQL3 соответствует качественным серьезным преобразованиям. В SQL3 введены новые типы данных, при этом предполагается возможность задания сложных структурированных типов данных, которые в большей степени соответствуют объектной ориентации. Наконец, добавлен раздел, который вводит стандарты на события и триггеры, которые ранее не затрагивались в стандартах, хотя давно уже широко использовались в коммерческих СУБД. В стандарте определены возможности четкой спецификации триггеров как совокупности события и действия. В качестве действия могут выступать не только последовательность операторов SQL, но и операторы управления ходом выполнения программы. В рамках управления транзакциями произошел возврат к старой модели транзакций, допускающей точки сохранения (savepoints). Возможность указания в операторе отката ROOLBACK точек возврата позволит откатывать транзакцию не в начало, а в промежуточную ранее сохраненную точку. Такое решение повышает гибкость реализации сложных алгоритмов обработки информации.
В настоящее время действует стандарт, принятый в 2003 году (SQL:2003) с небольшими модификациями, внесёнными позже в 2008 г.

История версий SQL

Год Название Иное название Изменения
SQL-86 SQL-87 Первый вариант стандарта, принятый институтом ANSI и одобренный ISO в 1987 году.
SQL-89 FIPS 127-1 Немного доработанный вариант предыдущего стандарта.
SQL-92 SQL2, FIPS 127-2 Значительные изменения (ISO 9075); уровень Entry Level стандарта SQL-92 был принят как стандарт FIPS 127-2.
SQL:1999 SQL3 Добавлена поддержка регулярных выражений, рекурсивных запросов, поддержка триггеров, базовые процедурные расширения, нескалярные типы данных и некоторые объектно-ориентированные возможности.
SQL:2003 Введены расширения для работы с XML-данными, оконные функции (применяемые для работы с OLAP-базами данных), генераторы последовательностей и основанные на них типы данных.
SQL:2006 Функциональность работы с XML-данными значительно расширена. Появилась возможность совместно использовать в запросах SQL и XQuery.
SQL:2008 Улучшены возможности оконных функций, устранены некоторые неоднозначности стандарта SQL:2003

А зачем вообще нужны эти стандарты? Зачем их изобретают и почему надо изучать их? Текст стандарта SQL2 занимает 600 станиц сухого формального текста, это очень много, и кажется, что это просто происки разработчиков стандартов, а не то, что необходимо рядовым разработчикам. Однако ни один серьезный разработчик, работающий с базами данных, не должен игнорировать стандарт, и для этого существуют весьма веские причины. Разработка любой информационной системы, ориентированной на технологию баз данных (а других информационных систем на настоящий момент практически нет), является трудоемким процессом, занимающим несколько десятков и даже сотен человеко-месяцев. Следует отдавать себе отчет, что нельзя разработать сколько-нибудь серьезную систему за несколько дней. Кроме того, развитие вычислительной техники, систем телекоммуникаций и программного обеспечения столь стремительно, что проект может устареть еще до момента внедрения. Но развивается не только вычислительная техника, изменяются и реальные объекты, поведение которых моделируется использованием как самой БД, так и процедур обработки информации в ней, то есть конкретных приложений, которые составляют реальное наполнение разрабатываемой информационной системы. Именно поэтому проект информационной системы должен быть рассчитан на расширяемость и переносимость на другие платформы. Большинство поставщиков аппаратуры и программного обеспечения следуют стратегии поддержки стандартов, в противном случае пользователи просто не будут их покупать. Однако каждый поставщик cтремится улучшить свой продут введением дополнительных возможностей, не входящих в стандарт. Выбор разработчиков, следовательно, таков: ориентироваться только на экзотические особенности данного продукта либо стараться в основном придерживаться стандарта. Во втором случае весь интеллектуальный труд, вкладываемый в разработку, становится более защищенным, так как система приобретает свойства переносимости. И в случае появления более перспективной платформы проект, ориентированный в большей степени на стандарты, может быть легче перенесен на нее, чем тот, который в основном ориентировался на особенности конкретной платформы. Кроме того, стандарты — это верный ориентир для разработчиков, так как все поставщики СУБД в своих перспективных разработках обязательно следуют стандарту, и можно быть уверенным, что в конце концов стандарт будет реализован практически во всех перспективных СУБД. Так произошло со стандартом SQL1, так происходит со стандартом SQL2 и так будет происходить со стандартом SQL3.

Для поставщиков СУБД стандарт — это путеводная звезда, которая гарантирует правильное направление работ. А вот эффективность реализации стандарта — это гарантия успеха.

SQL нельзя в полной мере отнести к традиционным языкам программирования, он не содержит традиционные операторы, управляющие ходом выполнения программы, операторы описания типов и многое другое, он содержит только набор стандартных операторов доступа к данным, хранящимся в базе данных. Операторы SQL встраиваются в базовый язык программирования, которым может быть любой стандартный язык типа C++, PL, COBOL и т. д. Кроме того, операторы SQL могут выполняться непосредственно в интерактивном режиме.

к оглавлению

к оглавлению

Структура SQL

В отличие от реляционной алгебры, где были представлены только операции запросов к БД, SQL является полным языком, в нем присутствуют не только операции запросов, но и операторы, соответствующие DDL — Data Definition Language — языку описания данных. Кроме того, язык содержит операторы, предназначенные для управления (администрирования ) БД. SQL содержит разделы, представленные в таблице 5.1:

Таблица 5.1. Операторы определения данных DDL

Оператор Смысл Действие
CREATE TABLE Создать таблицу Создает новую таблицу в БД
DROP TABLE Удалить таблицу Удаляет таблицу из БД
ALTER TABLE Изменить таблицу Изменяет структуру существующей таблицы или ограничения целостности, задаваемые для данной таблицы
CREATE VIEW Создать представление Создает виртуальную таблицу, соответствующую некоторому SQL-запросу
ALTER VIEW /Изменить представление Изменяет ранее созданное представление
DROP VIEW Удалить представление Удаляет ранее созданное представление
CREATE INDEX Создать индекс Создает индекс для некоторой таблицы для обеспечения быстрого доступа по атрибутам, входящим в индекс
DROP INDEX Удалить индекс Удаляет ранее созданный индекс

Таблица 5.2. Операторы манипулирования данными Data Manipulation Language (DML)

Оператор Смысл Действие
DELETE Удалить строки Удаляет одну или несколько строк, соответствующих условиям фильтрации, из базовой таблицы. Применение оператора согласуется с принципами поддержки целостности, поэтому этот оператор не всегда может быть выполнен корректно, даже если синтаксически он записан правильно
INSERT Вставить строку Вставляет одну строку в базовую таблицу. Допустимы модификации оператора, при которых сразу несколько строк могут быть перенесены из одной таблицы или запроса в базовую таблицу
UPDATE Обновить строку Обновляет значения одного или нескольких столбцов в одной или нескольких строках, соответствующих условиям фильтрации

Таблица 5.3. Языкзапросов Data Query Language (DQL)

Оператор Смысл Действие
SELECT Выбрать строки Оператор, заменяющий все операторы реляционной алгебры и позволяющий сформировать результирующее отношение, соответствующее запросу

Таблица 5.4. Средства управления транзакциями

Оператор Смысл Действие
COMMIT Завершить транзакцию Завершить комплексную взаимосвязанную обработку информации, объединенную в транзакцию
ROLLBACK Откатить транзакцию Отменить изменения, проведенные в ходе выполнения транзакции
'SAVEPOINT Сохранить промежуточную точку выполнения транзакции Сохранить промежуточное состояние БД, пометить его для того, чтобы можно было в дальнейшем к нему вернуться

Таблица 5.5. Средства администрирования данных

Оператор Смысл Действие
ALTER DATABASE Изменить БД Изменить набор основных объектов в базе данных, ограничений, касающихся всем базы данных
ALTER -DBAREA Изменить область хранения БД Изменить ранее созданную область храпения

Таблица 5.5(продолжение)

Оператор Смысл Действие
ALTER PASSWORD Изменить пароль Изменить пароль для всей базы данных
CREATE DATABASE Создать БД Создать новую базу данных, определив основные параметры для нее
CREATE DBAREA Создать область хранения Создать новую область хранения и сделать ее доступной для размещения данных
DROP DATABASE Удалить БД Удалить существующую базу данных (только в том случае, когда вы имеете право выполнить это действие)
DROP DBAREA Удалить область хранения БД Удалить существующую область хранения (если в ней на настоящий момент не располагаются активные данные)
GRANT Предоставить права Предоставить нрава доступа на ряд действий над некоторым объектом БД
REVOKE Лишить прав Лишить прав доступа к некоторому объекту или некоторым действиям над объектом

Таблица 5.6. Программный SQL

Оператор Смысл Действие
DECLARE Определяет курсор для запроса Задает некоторое имя и определяет связанный с ним запрос к БД, который соответствует виртуальному набору данных
OPEN Открыть курсор Формирует виртуальный набор данных, соответствующий описанию указанного курсора и текущему состоянию БД
FETCH Считать строку из множества строк, определенных курсором Считывает очередную строку, заданную параметром команды из виртуального набора данных, соответствующего открытому курсору
CLOSE Закрыть курсор Прекращает доступ к виртуальному набору данных, соответствующему указанному курсору
PREPARE Подготовить оператор SQL к динамическому выполнению Сгенерировать план выполнения запроса, соответствующего заданному оператору SQL

Оператор Смысл Действие
EXECUTE Выполнить оператор SQL, ранее подготовленный к динамическому выполнению Выполняет ранее подготовленный план запроса

В коммерческих СУБД набор основных операторов расширен. В большинство СУБД включены операторы определения и запуска хранимых процедур и операторы определения триггеров.

к оглавлению

Типы данных SQL

В языке SQL/89 поддерживаются следующие типы данных:

Несмотря на то, что в стандарте SQL1 не определяется точно, что подразумевается под типом INT и SMALLINT (это отдано на откуп реализации), указано только соотношение между этими типами данных, в большинстве реализаций тип данных INTEGER соответствует целым числам, хранимым в четырех байтах, a SMALLINT — соответствует целым числам, хранимым в двух байтах. Выбор одного из этих типов определяется размером числа.

В стандарте SQL92 добавлены следующие типы данных:

Большинство коммерческих СУБД поддерживают еще дополнительные типы данных, которые не специфицированы в стандарте. Так, например, практически все СУБД в том или ином виде поддерживают тип данных для представления неструктурированного текста большого объема. Этот тип аналогичен типу MEMO в настольных СУБД. Называются эти типы по-разному, например в ORACLE этот тип называется LONG, в DB2 - LONG VARCHAR, в SYBASE и MS SQL Server - TEXT.

Однако следует отметить, что специфика реализации отдельных типов данных серьезным образом влияет на результаты запросов к БД. Особенно это касается реализации типов данных DATE и TIMESTAMP. Поэтому при переносе приложений будьте внимательны, на разных платформах они могут работать по-разному, и одной из причин может быть различие в интерпретации типов данных.

При выполнении сравнений в операциях фильтрации могут использоваться константы заданных типов. В стандарте определены следующие константы. Для числовых типов данных определены константы в виде последовательности цифр с необязательным заданием знака числа и десятичной точкой. То есть правильными будут константы:

213-314 612.716 + 551.702

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

2.9Е-4 -134.235Е7 0.54267Е18

Строковые константы должны быть заключены в одинарные кавычки:

'Крылов Ю.Д.' 'Санкт-Петербург'

В некоторых реализациях, например MS SQL Server и Informix, допустимы двойные кавычки в строковых константах:

"Москва" "New York"

Однако следует отметить, что использование двойных кавычек может вызвать дополнительные проблемы при переносе приложений на другую платформу, поэтому мы рекомендуем по возможности избегать такого представления символьных констант.

Константы даты, времени и временного интервала в реляционных СУБД представляются в виде строковых констант. Форматы этих констант отличаются в различных СУБД. Кроме того, формат представления даты различен в разных странах. В большинстве СУБД реализованы способы настройки форматов представления дат или специальные функции преобразования форматов дат, как сделано, например, в СУБД ORACLE. Приведем примеры констант в MS SQL Server:

March 15, 1999 Маг 15 1999 3/15/1999 3-15-99 1999 MAR 15

В СУБД ORACLE та же константа запишется как

15-MAR-99

Кроме пользовательских констант в СУБД могут существовать и специальные системные константы. Стандарт SQL1 определяет только одну системную константу USER, которая соответствует имени пользователя, под которым вы подключились к БД.

В операторах SQL могут использоваться выражения, которые строятся по стандартным правилам применения знаков арифметических операций сложения (+), вычитания (-), умножения (*) и деления (/). Однако в ряде СУБД операция деления (/) интерпретируется как деление нацело, поэтому при построении сложных выражений вы можете получить результат, не соответствующий традиционной интерпретации выражения. В стандарт SQL2 включена возможность выполнения операций сложения и вычитания над датами. В большинстве СУБД также определена операция конкатенации над строковыми данными, обозначается она, к сожалению, по-разному. Так, например, для DB2 операция конкатенации обозначается двойной вертикальной чертой, в MS SQL Server — знаком сложения (+), поэтому два выражения, созданные в разных СУБД, эквивалентны:

'Mr./Mrs. '| | NAME || '' LAST_NAME

'Mr./Mrs. ' + NAME + ' ' LAST_NAME

В стандарте SQL1 не были определены встроенные функции, однако в большинстве коммерческих СУБД такие функции были реализованы, и в стандарт SQL2 уже введен ряд стандартных встроенных функций:

к оглавлению

Оператор выбора SELECT

Язык запросов (Data Query Language) в SQL состоит из единственного оператора SELECT. Этот единственный оператор поиска реализует все операции реляционной алгебры. Как просто, всего один оператор. Однако писать запросы на языке SQL (грамотные запросы) сначала совсем не просто. Надо учиться, так же как надо учиться решать математические задачки или составлять алгоритмы для решения непростых комбинаторных задач. Один и тот же запрос может быть реализован несколькими способами, и, будучи все правильными, они, тем не менее, могут существенно отличаться по времени исполнения, и это особенно важно для больших баз данных.

Синтаксис оператора SELECT имеет следующий вид:

SELECT [ALL | DISTINCT] «писокполей>|*)

FROM

[WHERE ]

[GROUP BY ]

[HAVING ]

[ORDER BY ]

Здесь ключевое слово ALL означает, что в результирующий набор строк включаются все строки, удовлетворяющие условиям запроса. Значит, в результирующий набор могут попасть одинаковые строки. И это нарушение принципов теории отношений (в отличие от реляционной алгебры, где по умолчанию предполагается отсутствие дубликатов в каждом результирующем отношении). Ключевое слово DISTINCT означает, что в результирующий набор включаются только различные строки, то есть дубликаты строк результата не включаются в набор.

Символ *. (звездочка) означает, что в результирующий набор включаются все столбцы из исходных таблиц запроса.

В разделе FROM задается перечень исходных отношений (таблиц) запроса.

В разделе WHERE задаются условия отбора строк результата или условия соединения кортежей исходных таблиц, подобно операции условного соединения в реляционной алгебре.

В разделе GROUP BY задается список полей группировки.

В разделе HAVING задаются предикаты-условия, накладываемые на каждую группу.

В части ORDER BY задается список полей упорядочения результата, то есть список полей, который определяет порядок сортировки в результирующем отношении. Например, если первым полем списка будет указана Фамилия, а вторым Номер группы, то в результирующем отношении сначала будут собраны в алфавтном порядке студенты, и если найдутся однофамильцы, то они будут расположены в порядке возрастания номеров групп.

В выражении условий раздела WHERE могут быть использованы следующие предикаты:

По стандарту в шаблон могут быть включены специальные символы:

IS NULL и IS NOT NULL.

Если в данном кортеже (в данной строке) указанный атрибут имеет неопределенное значение, то предикат IS NULL принимает значение «Истина» (TRUE), а предикат IS NOT NULL — «Ложь» (FALSE), в противном случае предикат IS NULL принимает значение «Ложь», а предикат IS NOT NULL принимает значение «Истина».

Введение Null-значений вызвало необходимость модификации классической двузначной логики и превращения ее в трехзначную. Все логические операции, производимые с неопределенными значениями, подчиняются этой логике в соответствии с заданной таблицей истинности:

А В Not A А ^ В A B
TRUE TRUE FALSE TRUE TRUE
TRUE FALSE FALSE FALSE TRUE
TRUE Null FALSE Null TRUE
FALSE TRUE TRUE FALSE TRUE
FALSE FALSE TRUE FALSE FALSE
FALSE Null TRUE FALSE Null
Null TRUE Null Null TRUE
Null FALSE Null FALSE Null
Null Null Null Null Null

В условиях поиска могут быть использованы все рассмотренные ранее предикаты.

Отложив на время знакомство с группировкой, рассмотрим детально первые три строки оператора SELECT:

Все последующие разделы оператора SELECT являются необязательными.

Самый простой запрос SELECT без необязательных частей соответствует просто декартову произведению. Например, выражение

SELECT *

FROM Rl. R2

соответствует декартову произведению таблиц R1 и R2. Выражение

SELECT Rl.A, R2.B

FROM Rl. R2

соответствует проекции декартова произведения двух таблиц на два столбца А из таблицы R1 и В из таблицы R2, при этом дубликаты всех строк сохранены, в отличие от операции проектирования в реляционной алгебре, где при проектировании по умолчанию все дубликаты кортежей уничтожаются.

Рассмотрим базу данных, которая моделирует сдачу сессии в некотором учебном заведении. Пусть она состоит из трех отношений R1, R2, R3. Будем считать, что они представлены таблицами Rl, R2 и R3 соответственно.

R1 = (ФИО, Дисциплина, Оценка);

R2 = (ФИО, Группа);

R3 = (Группы, Дисциплина )

R1
ФИО Дисциплина Оценка
Петров Ф. И. Базы данных
Сидоров К. А. Базы данных
Миронов А. В. Базы данных
Степанова К. Е. Базы данных
Крылова Т. С. Базы данных
Сидоров К. А. Теория информации
Степанова К. Е. Теория информации
Крылова Т. С. Теория информации

R1
ФИО Дисциплина Оценка
Миронов А. В. Теория информации Null
Владимиров В. А. Базы данных
Трофимов П. А. Сети и телекоммуникации
Иванова Е. А. Сети и телекоммуникации
Уткина Н. В. Сети и телекоммуникации
Владимиров В. А. Английский язык
Трофимов П. А. Английский язык
Иванова Е. А. Английский язык
Петров Ф. И. Английский язык

R2
ФИО Группа
Петров Ф. И.
Сидоров К. А.

yazvennaya-bolezn-zheludka-i-12-perstnoj-kishki-u-detej.html
yazvennaya-bolezn-zheludka-i-12-ti-perstnoj-kishki.html
    PR.RU™