Команды SQL: основные команды по группам DDL, DML, DQL с примерами
Основные команды SQL в одной таблице: DQL, DML, DDL, DCL и TCL. Что делает каждая, примеры с результатами и какие команды нужны аналитику в первую очередь.
Содержание статьи
В первую рабочую неделю аналитик открывает чужой скрипт и видит подряд begin, create temp table, insert into … select, with, grant. Слов полтора десятка, и на вид они равноправны. На деле у них разная цена: одни только читают данные и ничего не могут испортить, другие меняют строки, третьи убирают таблицу целиком. Ниже карта языка: какие команды есть в SQL, на какие пять групп их делят, что делает каждая и какие из них понадобятся сразу, а какие достаточно узнавать в лицо.
Коротко
Команда SQL — законченная инструкция базе данных: прочитать строки, изменить их, создать таблицу, выдать права или зафиксировать изменения. По назначению команды делят на пять групп.
- DQL — чтение:
SELECTсо всеми его частями (FROM,WHERE,GROUP BY,HAVING,ORDER BY,LIMIT,JOIN). Данные не меняются. - DML — изменение строк:
INSERT,UPDATE,DELETE. - DDL — структура базы:
CREATE,ALTER,DROP,TRUNCATE. - DCL — доступ:
GRANT,REVOKE. - TCL — транзакции:
BEGIN,COMMIT,ROLLBACK. - Аналитик почти всё время проводит в первой группе. Остальные нужны, чтобы собрать временную таблицу, прочитать чужой скрипт и не испортить данные.
- Примеры чтения из статьи выполняются в песочнице как есть. Примеры записи запускайте в своём PostgreSQL: они работают на копиях таблиц и заканчиваются откатом.
Какие команды есть в SQL: пять групп
Деление на группы — не часть языка, а способ его запомнить. База не знает слова DML, она просто выполняет UPDATE. Но группа сразу сообщает главное: что команда трогает — строки, структуру, права или границы транзакции. От этого зависит, чего от неё ждать и можно ли её отменить.
В учебниках группы называют по-разному. Документация MySQL и Oracle относит SELECT к DML, вместе с INSERT и UPDATE. Отдельную группу DQL (Data Query Language) выделяют, чтобы подчеркнуть разницу: запрос на чтение ничего не меняет. TRUNCATE тоже кочует. По смыслу он удаляет строки, как DML, но устроен как команда над таблицей целиком, и его обычно записывают в DDL. На собеседовании достаточно назвать группу и объяснить почему.
Со словами «команда» и «оператор» путаница похожая. В русских учебниках оператором называют и целую инструкцию (оператор SELECT), и знак внутри неё: операторы сравнения = и <>, логические AND и OR. В этой статье команда — инструкция целиком, а WHERE или GROUP BY — её части; по-английски это statement и clause. Знаки сравнения и логические связки разобраны отдельно, в статье про операторы сравнения, AND, OR и NOT.
Регистр на выполнение не влияет: SELECT, select и Select — одно и то же. В тексте команды набраны заглавными, чтобы их было видно, в коде — строчными, как во всех примерах этого блога.
| Группа | Команды | Что делают | Что меняется в базе | Нужны аналитику |
|---|---|---|---|---|
| DQL — Data Query Language, запросы | SELECT и его части: FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, JOIN, UNION, CASE, WITH | читают строки и считают по ним | ничего | каждый день |
| DML — Data Manipulation Language, изменение данных | INSERT, UPDATE, DELETE | добавляют, меняют и удаляют строки | содержимое таблиц | время от времени: свои таблицы и витрины |
| DDL — Data Definition Language, определение структуры | CREATE, ALTER, DROP, TRUNCATE | создают, меняют, удаляют и очищают таблицы и другие объекты | структура базы | время от времени: временные таблицы и представления |
| DCL — Data Control Language, управление доступом | GRANT, REVOKE | выдают и отзывают права | кто что может делать | редко: чаще читать, чем писать |
| TCL — Transaction Control Language, транзакции | BEGIN, COMMIT, ROLLBACK, SAVEPOINT | объединяют несколько команд в одно действие, которое можно отменить | граница, до которой изменения ещё отменяются | каждый раз, когда меняете данные |
DQL: SELECT и его части
В DQL одна команда — SELECT. Всё остальное, что новичок принимает за отдельные команды (WHERE, ORDER BY, LIMIT), — её части, и сами по себе они не выполняются. Обязательно только слово SELECT. FROM называет таблицу, остальные части добавляют по необходимости, но строго в том порядке, который показан в таблице ниже.
Запрос под таблицей отвечает на вопрос «кто из партнёрского канала зарегистрировался последним». FROM берёт таблицу users, в ней 4613 строк. WHERE оставляет партнёрский канал и регистрации с 1 августа — 297 строк. ORDER BY ставит свежие сверху, LIMIT оставляет пять. Без ORDER BY база вправе вернуть любые пять строк из 297, поэтому LIMIT без сортировки годится только для того, чтобы посмотреть, как выглядят данные.
LIMIT есть в PostgreSQL, MySQL, ClickHouse и DuckDB, но не в стандарте языка. В SQL Server ту же работу делает TOP, в стандарте и в Oracle — FETCH FIRST.
Примеры чтения выполнены на учебной базе «Маяк»: пользователи, события, платежи и подписки. Это база песочницы Симулятора SQL-аналитика, запросы можно копировать туда без правок. Подробные разборы — в статьях про SELECT, FROM и WHERE, про условия в WHERE и про ORDER BY и LIMIT.
| Часть | Что делает | Пример |
|---|---|---|
SELECT | перечисляет столбцы и выражения результата | select channel, count(*) |
FROM | называет таблицу-источник | from users |
JOIN … ON | присоединяет вторую таблицу по условию | join payments as p on p.user_id = u.user_id |
WHERE | оставляет строки, где условие истинно | where channel = 'partner' |
GROUP BY | собирает строки в группы | group by channel |
HAVING | оставляет группы, которые прошли условие | having count(*) > 1000 |
ORDER BY | сортирует результат | order by signup_date desc |
LIMIT | оставляет первые N строк | limit 5 |
UNION ALL | ставит результат второго запроса под результат первого | select … union all select … |
CASE | выражение «если — то», стоит внутри других частей | case when country = 'RU' then … end |
WITH | даёт имя промежуточному запросу, пишется перед SELECT | with payers as (select …) |
select user_id, signup_date, channel, country
from users
where channel = 'partner'
and signup_date >= date '2026-08-01'
order by signup_date desc, user_id
limit 5;
-- первая строка: 4576 | 2026-08-29 | partner | RU
-- без LIMIT таких строк 297GROUP BY и HAVING: посчитать по группам
Отдельные строки аналитику нужны редко, чаще — числа по группам: пользователи по каналам, выручка по месяцам. GROUP BY делит строки на группы по значению столбца, а агрегатная функция (count, sum, avg, min, max) сворачивает каждую группу в одну строку.
HAVING — фильтр для уже посчитанных групп. От WHERE он отличается моментом: WHERE отбрасывает строки до группировки и про count(*) ничего не знает, HAVING работает после неё и видит агрегаты. В учебной базе четыре канала. Условие having count(*) > 1000 оставляет три, партнёрский с 814 пользователями не проходит.
Есть правило, о которое спотыкаются почти все: в SELECT рядом с агрегатом могут стоять только столбцы из GROUP BY. Запрос select channel, country, count(*) from users group by channel PostgreSQL не выполнит и ответит: column "users.country" must appear in the GROUP BY clause or be used in an aggregate function. В группе organic много стран, и база не знает, какую из них показать. Подробности — в статьях про GROUP BY и про HAVING.
select channel, count(*) as users
from users
group by channel
having count(*) > 1000
order by users desc;
-- organic | 1747
-- referral | 1037
-- paid_search | 1015
-- partner (814) условие не прошёлJOIN: соединить две таблицы
Данные лежат в разных таблицах: каналы — в users, деньги — в payments. JOIN приставляет к строке одной таблицы подходящие строки другой. Какие считать подходящими, говорит условие после ON; чаще всего это равенство ключей, p.user_id = u.user_id.
Обычный JOIN (он же INNER JOIN) оставляет только строки, у которых нашлась пара. Платили 912 пользователей из 4613, остальные 3701 в запрос ниже не попадают, и для подсчёта выручки это правильно. Когда нужны все пользователи, включая тех, кто не платил, берут LEFT JOIN: он сохраняет левую таблицу целиком, а на месте отсутствующей пары ставит NULL.
После соединения строка результата — это уже платёж, а не пользователь: у одного человека платежей может быть несколько. Поэтому плательщиков считает count(distinct u.user_id), а платежи — count(*). В органике это 410 и 566. Перепутать два счёта — типичная ошибка с JOIN: запрос выполняется, число выглядит правдоподобно, но отвечает на другой вопрос. Виды соединений разобраны в статье про JOIN, ловушки левого соединения — в статье про LEFT JOIN.
select
u.channel,
count(distinct u.user_id) as payers,
count(*) as payments,
sum(p.amount) as revenue
from users as u
join payments as p on p.user_id = u.user_id
group by u.channel
order by revenue desc;
-- organic | 410 | 566 | 14114.00
-- referral | 278 | 384 | 9326.00
-- partner | 138 | 198 | 4732.00
-- paid_search | 86 | 103 | 2467.00CASE, WITH и UNION: собрать ответ из частей
CASE — не команда, а выражение: оно возвращает значение по условию и может стоять в SELECT, WHERE, GROUP BY и ORDER BY. Условия проверяются сверху вниз, срабатывает первое истинное. В первом примере пользователи разложены на три группы: Россия — 2715, другие страны — 1843, страна не указана — 55. Ветку is null нужно писать явно. Для пустого значения сравнение country = 'RU' не истинно, и без отдельной ветки такие строки ушли бы в else, к «другим странам».
WITH даёт имя промежуточному запросу, чтобы обращаться к нему как к таблице. Такой именованный шаг называют CTE. UNION ALL ставит результат одного запроса под результат другого: столбцов должно быть поровну, а типы должны совпадать по порядку. UNION без ALL ещё и убирает одинаковые строки, и там, где повторы были настоящими данными, итог тихо уменьшится.
Во втором примере шаг payers собирает тех, кто платил хотя бы раз, а два запроса, склеенные через UNION ALL, считают пользователей по обе стороны: 912 платили, 3701 нет. В сумме 4613 — столько строк в users. Такую сверку с итогом стоит делать всякий раз, когда вы делите таблицу на части. Подробнее: CASE WHEN, CTE и WITH, UNION и UNION ALL.
select
case
when country = 'RU' then 'Россия'
when country is null then 'страна не указана'
else 'другие страны'
end as region,
count(*) as users
from users
group by region
order by users desc;
-- Россия | 2715
-- другие страны | 1843
-- страна не указана | 55with payers as (
select distinct user_id
from payments
)
select 'платили' as segment, count(*) as users
from users
where user_id in (select user_id from payers)
union all
select 'не платили', count(*)
from users
where user_id not in (select user_id from payers);
-- платили | 912
-- не платили | 3701DML: INSERT, UPDATE, DELETE
Команды этой группы меняют содержимое таблиц, и результат остаётся в базе. Поэтому песочница Симулятора SQL-аналитика их не принимает: там разрешены только SELECT и WITH. Примеры записи выполнены в PostgreSQL 14 на копии той же учебной базы. Запускайте их в своём PostgreSQL. Каждый блок работает на временной копии таблицы и заканчивается командой rollback, так что исходные данные остаются прежними.
INSERT добавляет строки: либо перечисленные в VALUES, либо результат запроса (insert into … select). UPDATE меняет значения в существующих строках, DELETE удаляет строки. У двух последних есть WHERE, и от него зависит всё: без условия команда применится к каждой строке таблицы.
На каждую команду PostgreSQL отвечает числом затронутых строк, и это число — ваша проверка. В примере UPDATE 55 совпадает с числом пользователей без страны из предыдущего раздела, а DELETE 196 — с числом регистраций за первую неделю июня. Если вы ждали 55, а в ответе UPDATE 4613, условие не сработало, и пора писать rollback. Отдельные разборы: INSERT INTO, UPDATE, DELETE.
| Команда | Что делает | Ответ в примере |
|---|---|---|
INSERT | добавляет строки | INSERT 0 1 |
UPDATE | меняет значения в строках, которые прошли WHERE | UPDATE 55 |
DELETE | удаляет строки, которые прошли WHERE | DELETE 196 |
begin;
create temp table users_copy as select * from users; -- SELECT 4613
insert into users_copy (user_id, signup_date, channel, country, device)
values (5001, date '2026-08-31', 'organic', 'RU', 'desktop'); -- INSERT 0 1
update users_copy
set country = 'unknown'
where country is null; -- UPDATE 55
delete from users_copy
where signup_date < date '2026-06-08'; -- DELETE 196
select count(*) as rows_left
from users_copy; -- 4418 = 4613 + 1 - 196
rollback;DDL: CREATE, ALTER, DROP, TRUNCATE
DDL работает не со строками, а с объектами базы: таблицами, представлениями, индексами, схемами. CREATE создаёт объект, ALTER меняет его устройство, DROP удаляет вместе с содержимым. TRUNCATE стоит особняком: таблицу он оставляет, но разом убирает из неё все строки.
Аналитику из этой группы чаще всего нужны две формы CREATE. Первая — create temp table … as select …: сохранить промежуточный результат на время сессии и не пересчитывать тяжёлый запрос. Вторая — create view: дать имя запросу, которым пользуется вся команда; о разнице между ними — статья про VIEW и временные таблицы. Проектировать рабочие таблицы с ключами и ограничениями обычно поручают инженерам данных. Но читать CREATE TABLE нужно уметь: в нём записано, какие значения столбец вообще может принимать.
Три способа удаления путают постоянно. DELETE убирает выбранные строки, TRUNCATE — все строки сразу, DROP TABLE — саму таблицу. В примере после truncate запрос к plans возвращает 0 строк. Если повторить его после drop table, PostgreSQL ответит ошибкой relation "plans" does not exist.
В PostgreSQL команды DDL выполняются внутри транзакции и откатываются: в блоке ниже rollback отменил бы создание таблицы, даже если бы её не удалили. В MySQL и Oracle иначе: CREATE, ALTER и DROP фиксируют транзакцию сами, и отменить их нельзя. Подробнее: CREATE TABLE, ALTER TABLE, TRUNCATE.
begin;
create temp table plans (
plan text primary key,
price numeric(10, 2) not null
); -- CREATE TABLE
alter table plans
add column is_active boolean not null default true; -- ALTER TABLE
insert into plans (plan, price)
select distinct plan, amount from payments; -- INSERT 0 3
-- basic | 19.00 | t, pro | 29.00 | t, team | 39.00 | t
truncate table plans; -- TRUNCATE TABLE
select count(*) as rows_left from plans; -- 0: таблица есть, строк нет
drop table plans; -- DROP TABLE: таблицы больше нет
rollback;DCL: GRANT и REVOKE
DCL отвечает на вопрос, кому что можно. GRANT выдаёт право на действие с объектом, REVOKE отзывает. Права выдаются на конкретные команды — SELECT, INSERT, UPDATE, DELETE, TRUNCATE — и на конкретную таблицу, схему или базу. Получает их роль: в PostgreSQL ролью считается и отдельный пользователь, и группа пользователей.
Писать GRANT аналитику приходится редко, это работа администратора. Знать команду нужно по другой причине: с неё начинается разбор ошибки permission denied for table. Запрос верный, таблица существует, но вашей роли не выдали SELECT на неё. В заявку на доступ тогда стоит вписать готовую строку: grant select on payments to …. Верно и обратное: если рабочая учётная запись аналитика умеет только читать, случайный DELETE в продуктовой базе просто не выполнится.
В примере создаётся роль report_reader, получает право читать payments и теряет его. Функция has_table_privilege показывает, что право чтения не дало права удалять. Блоку нужны права на создание ролей. Он заканчивается командой rollback: роль и выданное право исчезают вместе с транзакцией.
begin;
create role report_reader; -- CREATE ROLE
grant select on payments to report_reader; -- GRANT
select
has_table_privilege('report_reader', 'payments', 'select') as can_read, -- t
has_table_privilege('report_reader', 'payments', 'delete') as can_delete; -- f
revoke select on payments from report_reader; -- REVOKE
select
has_table_privilege('report_reader', 'payments', 'select') as can_read; -- f
rollback;TCL: BEGIN, COMMIT, ROLLBACK
Транзакция — несколько команд, которые база выполняет как одно целое: применятся либо все, либо ни одна. BEGIN открывает транзакцию, COMMIT фиксирует изменения, ROLLBACK отменяет всё, что сделано после BEGIN. SAVEPOINT ставит внутри транзакции метку, к которой можно откатиться, не теряя начала.
Для аналитика транзакция — страховка. Вне транзакции psql и многие графические клиенты фиксируют каждую команду сразу после выполнения (режим autocommit), и неудачный DELETE можно вернуть только из резервной копии. После begin у вас появляется пауза: выполнить команду, посмотреть на число строк в ответе, сделать проверочный SELECT и только потом решить, commit или rollback.
В примере две транзакции. Первая создаёт временную копию платежей и фиксирует её командой commit. Вторая удаляет из копии 142 платежа тарифа team: внутри транзакции в таблице 1109 строк, после rollback — снова 1251. У незакрытой транзакции есть цена: она держит блокировки, и другие сессии могут ждать вашу. Открыли, проверили, закрыли. Уровни изоляции и блокировки разобраны в статье про транзакции.
begin;
create temp table payments_copy as select * from payments; -- SELECT 1251
commit; -- копия зафиксирована
begin;
delete from payments_copy where plan = 'team'; -- DELETE 142
select count(*) as rows_inside from payments_copy; -- 1109
rollback; -- удаление отменено
select count(*) as rows_after_rollback from payments_copy; -- 1251
drop table payments_copy;Какие команды нужны аналитику в первую очередь
Аналитик отвечает на вопросы по данным, которые собрал кто-то другой. Поэтому учить команды по группам подряд, от DDL к TCL, невыгодно: до первого полезного запроса вы доберётесь нескоро. Порядок лучше строить от задач.
Первые четыре шага в таблице — команды чтения, и на них уходит основная часть обучения. Синтаксис запоминается быстро. Дольше вырабатывается привычка проверять, что именно посчитал запрос, и она появляется только на задачах.
Тренироваться удобно в три захода. Сначала попробовать запросы руками в онлайн-редакторе SQL: там открыта учебная база авиакомпании и есть готовые примеры, которые подставляются в редактор. Затем решать задачи с проверкой: в каталоге задач по SQL они разложены по темам, от первых SELECT до JOIN и подзапросов. И пройти команды по порядку в курсе «SQL с нуля»: первые 10 уроков бесплатно, в них как раз шаг 1 — SELECT, сортировка, LIMIT и WHERE.
Когда команды чтения перестанут вызывать вопросы, имеет смысл перейти от упражнений к задачам, похожим на рабочие. В Симуляторе SQL-аналитика поручения приходят от коллег, и запрос там — способ ответить на вопрос, а не цель.
| Шаг | Команды | Что вы сможете после него |
|---|---|---|
| 1 | SELECT, FROM, WHERE, ORDER BY, LIMIT | посмотреть таблицу, найти нужные строки, собрать выгрузку |
| 2 | GROUP BY, агрегатные функции, HAVING | посчитать метрику по группам: пользователей по каналам, выручку по месяцам |
| 3 | JOIN, LEFT JOIN | связать пользователей с их событиями и платежами |
| 4 | CASE, WITH, UNION ALL | разметить сегменты и разложить длинный расчёт на шаги |
| 5 | CREATE TEMP TABLE … AS, CREATE VIEW, INSERT … SELECT | сохранить промежуточный результат и собрать свою витрину |
| 6 | BEGIN, COMMIT, ROLLBACK, UPDATE, DELETE | безопасно поправить данные в своей таблице |
| 7 | ALTER, DROP, TRUNCATE, GRANT | читать чужие скрипты и понимать ошибки доступа |
Частые ошибки новичка
Почти все они происходят не от незнания команды, а от неверного представления о том, когда она выполняется и что затрагивает. Тексты ошибок ниже — ответы PostgreSQL 14 на учебной базе.
От ошибок с UPDATE и DELETE спасает одна привычка: перед командой записи выполнить select count(*) с тем же WHERE. Запрос под списком — проверка перед обновлением из раздела про DML. Он возвращает 55, и именно это число должна назвать команда UPDATE.
select count(*) as rows_to_update
from users
where country is null;
-- 55: столько же должна назвать команда UPDATE с этим условием- Части запроса в неверном порядке. На
select channel, count(*) from users group by channel where …PostgreSQL отвечаетsyntax error at or near "where". Порядок записи жёсткий:WHEREстоит передGROUP BY,HAVINGпосле него,ORDER BYиLIMITв конце. - Агрегат в
WHERE. Условиеwhere count(*) > 1000даёт ошибкуaggregate functions are not allowed in WHERE: на этом шаге группы ещё не собраны. Условие на посчитанное значение пишут вHAVING. - Двойные кавычки вокруг строки.
where channel = "organic"заканчивается ошибкойcolumn "organic" does not exist. Двойные кавычки обозначают имя столбца, строка пишется в одинарных. UPDATEилиDELETEбезWHERE. Команда выполнится без предупреждения и затронет все строки: на копииusersв ответе будетUPDATE 4613вместо ожидаемых 55.- Команда записи без транзакции. В режиме autocommit изменение фиксируется сразу. Сначала
begin, потом команда и проверка, и только после этогоcommit. DELETE,TRUNCATEиDROPкак синонимы. Удалить строки, очистить таблицу и удалить таблицу — три разных действия, и отменить их можно не во всякой базе.- Проверка только по отсутствию ошибки. Запрос, который выполнился, не обязательно посчитал то, о чём вы спрашивали:
JOINмог размножить строки,WHERE— отбросить пустые значения. Сверяйте итог с числом, которое знаете заранее: 912 + 3701 = 4613.
Частые вопросы
Сколько всего команд в SQL?
Единого числа нет: стандарт описывает одни инструкции, каждая СУБД добавляет свои, и в справочнике PostgreSQL их больше сотни. Для работы аналитика хватает полутора десятков из этой статьи, а основную часть времени занимает одна — SELECT.
Чем DDL отличается от DML?
DDL меняет структуру: какие таблицы и столбцы есть в базе. DML меняет содержимое: какие строки лежат в этих таблицах. alter table users add column … — DDL, update users set … — DML.
SELECT — это DML или DQL?
Зависит от классификации. Там, где групп четыре, SELECT относят к DML как команду работы с данными. Там, где пять, чтение выделяют в DQL. Спорить об этом не стоит. Важно свойство самой команды: SELECT данные не меняет.
TRUNCATE — это DDL или DML?
Обычно его относят к DDL: он не перебирает строки, а очищает таблицу целиком, у него нет WHERE, и права на него выдаются отдельно от DELETE. По результату он при этом похож на DELETE без условия.
Одинаковы ли команды SQL в разных базах данных?
Основа общая: SELECT, INSERT, UPDATE, DELETE, CREATE TABLE и JOIN пишутся почти одинаково в PostgreSQL, MySQL и SQL Server. Отличия в деталях: LIMIT или TOP, функции дат, откат команд DDL. Запрос из одной базы в другой обычно требует правок, но не переписывания.
Может ли SELECT испортить данные?
Обычный запрос на чтение строк не меняет. Худшее, что он может сделать, — выполняться долго и нагрузить базу. На больших таблицах начинайте с фильтра по дате и LIMIT.
Нужно ли аналитику знать DDL и DCL?
Писать — редко, читать — постоянно. Скрипты загрузки витрин состоят из CREATE, TRUNCATE и INSERT, а ошибка доступа объясняется одной строкой GRANT. Достаточно понимать, что делает каждая команда и что после неё уже не вернуть.
Что почитать дальше
Эта статья — карта: по каждой команде есть отдельный разбор с примерами и ловушками. Если нужен синтаксис без объяснений, откройте шпаргалку; если готовые запросы под типовые задачи — каталог примеров.
Материалы по теме

Порядок выполнения SQL-запроса: от FROM до LIMIT
Логический порядок выполнения SQL-запроса по шагам: FROM, WHERE, GROUP BY, HAVING, оконные функции, SELECT, DISTINCT, ORDER BY, LIMIT. Почему алиас не виден в WHERE.

TRUNCATE в SQL: как очистить таблицу и чем он отличается от DELETE
TRUNCATE TABLE в SQL и PostgreSQL: синтаксис, отличие от DELETE и DROP, RESTART IDENTITY, CASCADE и внешние ключи, откат в транзакции, права. Примеры с реальным выводом.

Шпаргалка по SQL для аналитика: синтаксис и типовые запросы
Шпаргалка по SQL на одной странице: порядок выполнения запроса, фильтры, GROUP BY, JOIN, CTE, оконные функции, даты, NULL и метрики. Каждый пример выполнен, результат указан.