{
  "assessmentTests": {
    "sql_test": {
      "name": "Тест SQL",
      "desc": "30 вопросов-сценариев на фильтрацию, соединения, агрегацию, подзапросы и оконные функции — узнайте, соответствует ли ваше знание SQL тому, что объявления о вакансиях имеют в виду под компетентностью в SQL.",
      "recommendation": "Ваш профиль навыков SQL",
      "results": {
        "beginner": {
          "name": "Начинающий",
          "desc": "Вы можете написать рабочий SELECT и отфильтровать по нескольким условиям, но вопросы, которые вы пропустили, группируются вокруг работы с NULL и механики соединений, а не синтаксиса — сравнение столбца с NULL через =, что на самом деле сохраняет LEFT JOIN, как работают границы BETWEEN. Это не о плохом знании баз данных; это специфичные для SQL правила, которые запутывают людей, учивших SQL методом проб и ошибок, а не по документации. Это важно на работе, потому что каждое из этих правил — место, где запрос возвращает правдоподобное, но неправильное количество строк.",
          "recommendation": "Начните с трёх вещей в таком порядке: почему WHERE col = NULL никогда не совпадает ни с чем (и требуется IS NULL), как INNER JOIN удаляет несовпадающие строки, а LEFT JOIN их сохраняет, и точные границы, которые включает BETWEEN. Mode Analytics' SQL учебник и документация PostgreSQL оба рассматривают все три с примерами, которые можно запустить."
        },
        "intermediate": {
          "name": "Средний уровень",
          "desc": "Вы комфортно справляетесь с повседневными запросами отчётности — соединения, GROUP BY, прямая фильтрация — и не будете затруднены рутинной работой на панелях или ad-hoc анализом. Разница между здесь и Продвинутым в основном в том, что происходит, когда предложения взаимодействуют: предложение WHERE на присоединённой таблице молча превращает LEFT JOIN обратно в INNER JOIN, соединение, которое размножает количество строк перед применением агрегата, NOT IN, который молча возвращает ничего, потому что в подзапрос попал один NULL. Это баги, которые проходят при беглом просмотре и появляются только когда кто-то сравнивает итог с другим отчётом.",
          "recommendation": "Сосредоточьтесь на том, как предложения взаимодействуют друг с другом, а не на том, что каждое делает отдельно: WHERE фильтрация на столбцах правой части LEFT JOIN, размножение строк из one-to-many соединений перед применением агрегата, и почему NOT IN не работает в присутствии NULL (EXISTS обычно работает). Затем HAVING vs WHERE, так как этот раздел запутывает людей, которые уже знают оба предложения отдельно."
        },
        "advanced": {
          "name": "Продвинутый",
          "desc": "Это уровень, который большинство объявлений о вакансиях имеют в виду под «сильный SQL». Вы читаете многосоединённый запрос и можете предсказать его количество строк перед запуском, вы знаете, почему набор результатов изменился, а не только что он изменился, и вы обращаетесь к CTE или оконной функции вместо вложенного подзапроса, когда это более понятный инструмент. То, что отделяет эту категорию от вершины, — это защитная сторона работы: знание того, какую ранжирующую функцию использовать, когда связи важны, что транзакция изолирует от одновременной сессии, и что отсутствующее правило ON DELETE действительно делает на уровне базы данных.",
          "recommendation": "Углубитесь в части, которые защищают данные, которыми занимаются другие люди: различие между RANK, DENSE_RANK и ROW_NUMBER при наличии связей, что COMMIT действительно делает видимым и для кого, и поведение внешних ключей при DELETE. Используйте глубокие погружения The Index newsletter или SQL for Devs по оконным функциям и изоляции транзакций как следующую остановку для обоих."
        },
        "expert": {
          "name": "Эксперт",
          "desc": "Вы набрали максимум во всех разделах — фильтрация и семантика NULL, соединения и операции над множествами, агрегация и подзапросы, оконные функции, CTE и ограничения. На практике это означает, что вам можно дать чужой многосоединённый запрос отчёта и объяснить, почему он возвращает именно то количество строк, которое возвращает, не только что синтаксис говорит, что он должен возвращать — это более сложный и ценный навык. На этом уровне язык запросов редко является ограничивающим фактором; обычно ограничение — это дизайн схемы или размер данных под ней.",
          "recommendation": "Возвраты теперь в планах выполнения и дизайне: чтение вывода EXPLAIN перед предположением, что запрос медленный, стратегия индексации как компромисс против стоимости записи, а не как бесплатный выигрыш, и решения по нормализации, которые выдержат рост схемы. Если вас отбирают на роль, опишите баг запроса вроде размножения соединения или ловушки NOT IN/NULL, которую вы нашли в production, а не называйте особенности SQL — это демонстрирует рассуждение, не только словарь."
        }
      },
      "questions": [
        {
          "question": "У таблицы есть столбец age, допускающий NULL. Вы запускаете SELECT * FROM users WHERE age = NULL. Сколько строк это возвращает, даже если несколько строк имеют age установленный на NULL?",
          "options": [
            {
              "icon": "",
              "label": "Каждую строку, где age равно NULL, потому что = совпадает с NULL как с любым другим значением"
            },
            {
              "icon": "",
              "label": "Ноль — сравнение чего-либо с NULL через = даёт UNKNOWN, никогда TRUE, поэтому ни одна строка не совпадает; требуется IS NULL"
            },
            {
              "icon": "",
              "label": "Синтаксическая ошибка — NULL не может появляться на правой стороне ="
            },
            {
              "icon": "",
              "label": "Каждую строку в таблице, потому что сравнения с NULL по умолчанию возвращают TRUE"
            }
          ]
        },
        {
          "question": "Вы запускаете SELECT DISTINCT department, role FROM employees. Что DISTINCT удаляет дубликаты?",
          "options": [
            {
              "icon": "",
              "label": "Комбинацию department и role вместе — строка удаляется только если обе колонки точно совпадают с другой строкой"
            },
            {
              "icon": "",
              "label": "Только дубликаты значений department, сохраняя каждый role"
            },
            {
              "icon": "",
              "label": "Только дубликаты значений role, сохраняя каждый department"
            },
            {
              "icon": "",
              "label": "Ничего — DISTINCT работает только с одним столбцом"
            }
          ]
        },
        {
          "question": "Столбец name фильтруется WHERE name LIKE 'A_'. Какое из этих значений совпадает: 'A', 'Al', 'Ana', 'Ally'?",
          "options": [
            {
              "icon": "",
              "label": "'A' — подчеркивание необязательно и совпадает с нулевым или большим количеством символов"
            },
            {
              "icon": "",
              "label": "'Al' — подчеркивание совпадает ровно с одним символом, поэтому LIKE 'A_' совпадает с любой двухсимвольной строкой, начинающейся с A"
            },
            {
              "icon": "",
              "label": "'Ana' и 'Ally' — подчеркивание совпадает с любым количеством символов в конце"
            },
            {
              "icon": "",
              "label": "Все четыре значения совпадают, потому что LIKE игнорирует длину"
            }
          ]
        },
        {
          "question": "Столбец price фильтруется WHERE price BETWEEN 10 AND 20. Включаются ли строки с price ровно 10 или ровно 20?",
          "options": [
            {
              "icon": "",
              "label": "Нет — BETWEEN исключает обе границы"
            },
            {
              "icon": "",
              "label": "Да — BETWEEN включает обе стороны, эквивалентно price >= 10 AND price <= 20"
            },
            {
              "icon": "",
              "label": "Только price = 10 включается; верхняя граница исключающая"
            },
            {
              "icon": "",
              "label": "Только price = 20 включается; нижняя граница исключающая"
            }
          ]
        },
        {
          "question": "Таблица orders имеет 10 строк, и 3 из них имеют NULL shipped_date. Что возвращает SELECT COUNT(*) FROM orders, и что возвращает SELECT COUNT(shipped_date) FROM orders?",
          "options": [
            {
              "icon": "",
              "label": "10, затем 10 — COUNT всегда считает строки независимо от NULL"
            },
            {
              "icon": "",
              "label": "7, затем 7 — обе формы пропускают строки с NULL"
            },
            {
              "icon": "",
              "label": "10, затем 7 — COUNT(*) считает каждую строку, COUNT(column) считает только строки, где этот столбец не NULL"
            },
            {
              "icon": "",
              "label": "10, затем 3 — COUNT(column) считает только значения NULL"
            }
          ]
        },
        {
          "question": "Вы запускаете SELECT name, salary FROM employees ORDER BY 2 DESC. На что указывает 2?",
          "options": [
            {
              "icon": "",
              "label": "Синтаксическую ошибку — ORDER BY принимает только названия столбцов, не номера"
            },
            {
              "icon": "",
              "label": "Вторую строку результата"
            },
            {
              "icon": "",
              "label": "Буквальное значение 2, используемое как тай-брейкер"
            },
            {
              "icon": "",
              "label": "Второй столбец в списке SELECT, salary — ORDER BY принимает номер позиции столбца как сокращение"
            }
          ]
        },
        {
          "question": "В стандартном SQL чему равно 'Ana' || ' ' || 'Lee'?",
          "options": [
            {
              "icon": "",
              "label": "Синтаксическая ошибка — SQL не имеет оператора конкатенации"
            },
            {
              "icon": "",
              "label": "3 — || рассматривается как логическое ИЛИ и возвращает счёт"
            },
            {
              "icon": "",
              "label": "'AnaLee' — || конкатенирует без сохранения пробелов"
            },
            {
              "icon": "",
              "label": "'Ana Lee' — || является стандартным SQL оператором конкатенации строк"
            }
          ]
        },
        {
          "question": "Какое из этих ровно эквивалентно WHERE status IN ('open', 'pending', 'review')?",
          "options": [
            {
              "icon": "",
              "label": "WHERE status = 'open' AND status = 'pending' AND status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status != 'open' OR status != 'pending' OR status != 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status = 'open' OR status = 'pending' OR status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status LIKE 'open,pending,review'"
            }
          ]
        },
        {
          "question": "customers имеет 100 строк; 20 из них никогда не размещали заказ. Вы запускаете SELECT c.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id. Появляются ли 20 клиентов без заказов в результате?",
          "options": [
            {
              "icon": "",
              "label": "Да, один раз каждый, с o.id показано как NULL"
            },
            {
              "icon": "",
              "label": "Да, но только если они также появляются в предложении WHERE"
            },
            {
              "icon": "",
              "label": "Нет — INNER JOIN возвращает только строки, имеющие совпадение в обеих таблицах, поэтому клиенты без заказов полностью удаляются"
            },
            {
              "icon": "",
              "label": "Да, дублированы один раз для каждого столбца в orders"
            }
          ]
        },
        {
          "question": "Вы хотите каждого клиента независимо от того, есть ли у них заказы, поэтому вы пишете SELECT c.id, o.total FROM customers c LEFT JOIN orders o ON c.id = o.customer_id WHERE o.total > 100. Возвращает ли это всё ещё клиентов с нулевыми заказами?",
          "options": [
            {
              "icon": "",
              "label": "Нет — фильтрация по o.total в предложении WHERE отбрасывает строки NULL, которые произвёл LEFT JOIN для несовпадающих клиентов, поэтому ведёт себя как INNER JOIN"
            },
            {
              "icon": "",
              "label": "Да — LEFT JOIN всегда сохраняет каждую строку от customers независимо от того, что следует"
            },
            {
              "icon": "",
              "label": "Да, с o.total показано как 0 для клиентов без заказов"
            },
            {
              "icon": "",
              "label": "Нет — LEFT JOIN молча преобразует себя в RIGHT JOIN, когда добавляется предложение WHERE"
            }
          ]
        },
        {
          "question": "Таблица employees имеет столбец id и столбец manager_id, который указывает на другую строку id. Чтобы перечислить каждого сотрудника рядом с именем его менеджера, вы соединяете таблицу с собой: SELECT e.name, m.name FROM employees e JOIN employees m ON e.manager_id = m.id. Как называется этот паттерн?",
          "options": [
            {
              "icon": "",
              "label": "Перекрёстное соединение — каждый сотрудник совпадает с каждым менеджером"
            },
            {
              "icon": "",
              "label": "Рекурсивное соединение — оно извлекает полную цепь управления"
            },
            {
              "icon": "",
              "label": "Самосоединение — одна и та же таблица соединяется с собой, используя два разных alias'а"
            },
            {
              "icon": "",
              "label": "Это неправильный SQL — таблица не может быть соединена с собой"
            }
          ]
        },
        {
          "question": "Две SELECT операции с одинаковыми столбцами объединяются с UNION. Если обе операции возвращают идентичную строку, сколько копий этой строки появляется в финальном результате?",
          "options": [
            {
              "icon": "",
              "label": "Одна — UNION удаляет дубликаты строк в объединённом результате; UNION ALL сохранил бы обе копии"
            },
            {
              "icon": "",
              "label": "Две — UNION сохраняет каждую строку из обеих операций"
            },
            {
              "icon": "",
              "label": "Ноль — UNION удаляет любую строку, которая появляется в обеих операциях"
            },
            {
              "icon": "",
              "label": "Это зависит от того, какая операция перечислила строку первой"
            }
          ]
        },
        {
          "question": "Таблица sizes имеет 3 строки и colors имеет 4 строки. Сколько строк возвращает SELECT * FROM sizes CROSS JOIN colors?",
          "options": [
            {
              "icon": "",
              "label": "7 — CROSS JOIN добавляет количество строк вместе"
            },
            {
              "icon": "",
              "label": "12 — CROSS JOIN возвращает каждую возможную комбинацию строк из обеих таблиц (3 x 4)"
            },
            {
              "icon": "",
              "label": "3 — CROSS JOIN возвращает одну строку для каждой строки первой таблицы"
            },
            {
              "icon": "",
              "label": "0 — CROSS JOIN требует условия ON иначе возвращает ничего"
            }
          ]
        },
        {
          "question": "orders имеет 1 строку для заказа #100, и order_items имеет 3 строки для заказа #100 (одна для каждого позиции). Вы запускаете SELECT o.id, o.total FROM orders o JOIN order_items i ON o.id = i.order_id WHERE o.id = 100. Сколько строк возвращается для заказа #100?",
          "options": [
            {
              "icon": "",
              "label": "3 — соединение производит одну выходную строку на каждую совпадающую строку order_items, поэтому единая строка заказа #100 повторяется один раз на позицию"
            },
            {
              "icon": "",
              "label": "1 — orders имеет только одну строку для заказа #100, поэтому соединение не может производить больше"
            },
            {
              "icon": "",
              "label": "4 — одна строка для заказа плюс одна на позицию"
            },
            {
              "icon": "",
              "label": "0 — соединение однострочной таблицы с трёхстрочной таблицей на неуникальный ключ не работает"
            }
          ]
        },
        {
          "question": "SELECT c.name, o.id FROM customers c RIGHT JOIN orders o ON c.id = o.customer_id возвращает те же строки, что и какое из этих?",
          "options": [
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o LEFT JOIN customers c ON c.id = o.customer_id — замена порядка таблиц и использование LEFT JOIN вместо RIGHT JOIN эквивалентны"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c LEFT JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o CROSS JOIN customers c"
            }
          ]
        },
        {
          "question": "В стандартном SQL вы запускаете SELECT department, name, AVG(salary) FROM employees GROUP BY department. Это правильный запрос?",
          "options": [
            {
              "icon": "",
              "label": "Да — GROUP BY нужно только включить department, потому что он перечислен первым"
            },
            {
              "icon": "",
              "label": "Нет — name выбран, но ни агрегирован, ни в GROUP BY, и стандартный SQL требует, чтобы каждый неагрегированный выбранный столбец появился в GROUP BY"
            },
            {
              "icon": "",
              "label": "Да — SQL автоматически выбирает одно произвольное имя на department"
            },
            {
              "icon": "",
              "label": "Нет — AVG() не может быть объединён с GROUP BY в одном запросе"
            }
          ]
        },
        {
          "question": "Вы хотите departments, чей средний salary превышает 80000. Какое предложение фильтрует агрегированное значение вроде AVG(salary) после группировки — WHERE или HAVING?",
          "options": [
            {
              "icon": "",
              "label": "WHERE — HAVING используется только с UNION запросами"
            },
            {
              "icon": "",
              "label": "Либо работает одинаково с функциями агрегации"
            },
            {
              "icon": "",
              "label": "Ни то, ни другое — фильтрация агрегатов требует подзапрос"
            },
            {
              "icon": "",
              "label": "HAVING — WHERE фильтрует отдельные строки перед группировкой, HAVING фильтрует группы после агрегации"
            }
          ]
        },
        {
          "question": "Вы пишете SELECT salary * 1.1 AS new_salary FROM employees WHERE new_salary > 50000. Это запустится?",
          "options": [
            {
              "icon": "",
              "label": "Да — псевдонимы, определённые в SELECT, всегда доступны WHERE в одном запросе"
            },
            {
              "icon": "",
              "label": "Да, но только для числовых alias'ов"
            },
            {
              "icon": "",
              "label": "Нет — AS не разрешён внутри WHERE-фильтруемого запроса"
            },
            {
              "icon": "",
              "label": "Нет — WHERE вычисляется перед SELECT присвоением alias'а new_salary, поэтому alias не существует на этом этапе выполнения"
            }
          ]
        },
        {
          "question": "SELECT name FROM employees e WHERE salary > (SELECT AVG(salary) FROM employees WHERE department = e.department). Почему это называется коррелированным подзапросом?",
          "options": [
            {
              "icon": "",
              "label": "Потому что использует JOIN вместо WHERE"
            },
            {
              "icon": "",
              "label": "Внутренний подзапрос ссылается на e.department из внешнего запроса, поэтому должен переоцениться для каждой строки, которую рассматривает внешний запрос"
            },
            {
              "icon": "",
              "label": "Потому что возвращает более одного столбца"
            },
            {
              "icon": "",
              "label": "Потому что он запускается ровно один раз перед тем, как начать внешний запрос"
            }
          ]
        },
        {
          "question": "Подзапрос SELECT manager_id FROM employees возвращает некоторые значения NULL вместе с реальными id. Вы запускаете SELECT name FROM employees WHERE id NOT IN (SELECT manager_id FROM employees). Что происходит?",
          "options": [
            {
              "icon": "",
              "label": "Возвращает каждого сотрудника, который не менеджер, игнорируя NULL"
            },
            {
              "icon": "",
              "label": "Возвращает ноль строк — один NULL в списке NOT IN делает каждое сравнение UNKNOWN, поэтому ни одна строка не может удовлетворить условию"
            },
            {
              "icon": "",
              "label": "Вызывает ошибку, потому что NOT IN не может использоваться с подзапросами"
            },
            {
              "icon": "",
              "label": "Возвращает каждого сотрудника, так как NULL рассматривается как подстановочный символ"
            }
          ]
        },
        {
          "question": "SELECT name FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id). Что это возвращает?",
          "options": [
            {
              "icon": "",
              "label": "Каждого клиента, у которого есть хотя бы одна строка в orders — EXISTS только проверяет, возвращает ли подзапрос какие-либо строки, не какие значения они содержат"
            },
            {
              "icon": "",
              "label": "Каждого клиента, потому что SELECT 1 всегда возвращает true"
            },
            {
              "icon": "",
              "label": "Ошибку, потому что подзапрос выбирает число вместо названия столбца"
            },
            {
              "icon": "",
              "label": "Только клиентов ровно с одним заказом"
            }
          ]
        },
        {
          "question": "Вы пишете SELECT name, (SELECT order_id FROM orders WHERE customer_id = c.id) AS last_order FROM customers c, и данный клиент имеет 3 строки в orders. Что происходит, когда запрос запускается?",
          "options": [
            {
              "icon": "",
              "label": "Возвращает первый совпадающий order_id и молча игнорирует два других"
            },
            {
              "icon": "",
              "label": "Возвращает список всех трёх order_id, разделённый запятыми"
            },
            {
              "icon": "",
              "label": "Возвращает 3 строки для этого клиента, одну на заказ"
            },
            {
              "icon": "",
              "label": "Вызывает ошибку во время выполнения — скалярный подзапрос в списке SELECT должен возвращать максимум одну строку, а этот возвращает три"
            }
          ]
        },
        {
          "question": "Четыре строки совпадают по самому высокому очку. Используя RANK() ORDER BY score DESC, все четыре получают rank 1. Какой rank получает следующая строка?",
          "options": [
            {
              "icon": "",
              "label": "5 — RANK() оставляет промежуток, равный количеству связанных строк, перед продолжением"
            },
            {
              "icon": "",
              "label": "2 — RANK() всегда увеличивается ровно на один после любой связи"
            },
            {
              "icon": "",
              "label": "1 — каждая последующая строка также получает rank 1"
            },
            {
              "icon": "",
              "label": "4 — RANK() перезапускает подсчёт с количества связей"
            }
          ]
        },
        {
          "question": "Четыре строки совпадают по самому высокому очку. Используя DENSE_RANK() ORDER BY score DESC, все четыре получают rank 1. Какой rank получает следующая строка?",
          "options": [
            {
              "icon": "",
              "label": "5 — DENSE_RANK() ведёт себя точно как RANK()"
            },
            {
              "icon": "",
              "label": "1 — DENSE_RANK() присваивает один и тот же rank каждой оставшейся строке"
            },
            {
              "icon": "",
              "label": "2 — DENSE_RANK() никогда не оставляет промежутков, поэтому следующее отличное значение всегда получает следующий последовательный rank"
            },
            {
              "icon": "",
              "label": "3 — DENSE_RANK() пропускает один rank на каждую группу связей"
            }
          ]
        },
        {
          "question": "SUM(amount) OVER (PARTITION BY region ORDER BY amount) добавляется в запрос. Что PARTITION BY region делает здесь?",
          "options": [
            {
              "icon": "",
              "label": "Перезапускает текущую сумму отдельно для каждого region вместо накопления по всему результату"
            },
            {
              "icon": "",
              "label": "Фильтрует результаты в один region"
            },
            {
              "icon": "",
              "label": "Группирует и схлопывает строки в одну на region, как GROUP BY"
            },
            {
              "icon": "",
              "label": "Сортирует region'ы буквально перед суммированием"
            }
          ]
        },
        {
          "question": "ROW_NUMBER() OVER (ORDER BY score DESC) применяется к пяти строкам, две из которых точно совпадают по очку. Могут ли две строки когда-либо получить один и тот же номер строки?",
          "options": [
            {
              "icon": "",
              "label": "Да — связанные строки всегда делят один номер строки"
            },
            {
              "icon": "",
              "label": "Только если PARTITION BY также используется"
            },
            {
              "icon": "",
              "label": "Это зависит от того, находится ли связь в первой или последней позиции"
            },
            {
              "icon": "",
              "label": "Нет — ROW_NUMBER() всегда присваивает уникальное, строго возрастающее целое число каждой строке, даже когда значения связаны"
            }
          ]
        },
        {
          "question": "WITH high_earners AS (SELECT * FROM employees WHERE salary > 100000) SELECT department, COUNT(*) FROM high_earners GROUP BY department. Что такое high_earners?",
          "options": [
            {
              "icon": "",
              "label": "Общее табличное выражение (CTE) — именованный, временный набор результатов, на который может ссылаться остальная часть запроса как на таблицу"
            },
            {
              "icon": "",
              "label": "Постоянная таблица, созданная в базе данных"
            },
            {
              "icon": "",
              "label": "Представление, которое сохраняется после завершения запроса"
            },
            {
              "icon": "",
              "label": "Сохранённая процедура, которая должна быть вызвана отдельно"
            }
          ]
        },
        {
          "question": "Столбец объявлен PRIMARY KEY. Можете ли вы вставить строку, где этот столбец NULL?",
          "options": [
            {
              "icon": "",
              "label": "Да — PRIMARY KEY только обеспечивает уникальность, не NULL-ность"
            },
            {
              "icon": "",
              "label": "Да, но только одна NULL строка разрешена, как с ограничением UNIQUE"
            },
            {
              "icon": "",
              "label": "Нет — столбец PRIMARY KEY неявно NOT NULL, поэтому вставка NULL отклоняется"
            },
            {
              "icon": "",
              "label": "Это зависит от того, имеет ли столбец также значение по умолчанию"
            }
          ]
        },
        {
          "question": "Внутри открытой транзакции вы запускаете UPDATE но ещё не запустили COMMIT. Видна ли эта обновка из второго, отдельного соединения с базой данных?",
          "options": [
            {
              "icon": "",
              "label": "Да — все соединения видят каждую запись в момент её запуска"
            },
            {
              "icon": "",
              "label": "Да, но только если второе соединение тоже откроет транзакцию"
            },
            {
              "icon": "",
              "label": "Это зависит только от того, какая таблица была обновлена"
            },
            {
              "icon": "",
              "label": "Нет — незафиксированное изменение видимо только внутри транзакции, которая его произвела, пока COMMIT не сделает его долговечным и видимым для других"
            }
          ]
        },
        {
          "question": "products.category_id имеет ограничение FOREIGN KEY, ссылающееся на categories.id. Вы пытаетесь DELETE строку из categories, на которую всё ещё указывают products, без указанного правила ON DELETE. Что происходит?",
          "options": [
            {
              "icon": "",
              "label": "Строка category удаляется и совпадающие значения products.category_id автоматически устанавливаются на NULL"
            },
            {
              "icon": "",
              "label": "Строка category удаляется и каждый product, который ссылался на неё, также удаляется"
            },
            {
              "icon": "",
              "label": "DELETE отклоняется — поведение внешнего ключа по умолчанию блокирует удаление referenced строки пока на неё ещё указывают зависимые строки"
            },
            {
              "icon": "",
              "label": "DELETE удаётся молча, оставляя category_id products'ов указывающим на несуществующий category"
            }
          ]
        }
      ],
      "optionOrderVersion": "e0466e547cfd8a45"
    }
  }
}
