Использовал ли ты вложенные запросы (подзапросы) в SQL?

Ответ

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

Основные типы и примеры использования:

  1. Скалярный подзапрос (в SELECT, WHERE, HAVING). Возвращает одно значение.

    -- Найти всех пользователей, чей доход выше среднего по компании
    SELECT name, salary
    FROM employees
    WHERE salary > (SELECT AVG(salary) FROM employees);
  2. Подзапрос, возвращающий множество значений (с IN, ANY, ALL, EXISTS).

    -- Найти товары, которые никогда не заказывались
    SELECT id, name
    FROM products p
    WHERE NOT EXISTS (
        SELECT 1 FROM order_items oi WHERE oi.product_id = p.id
    );
  3. Подзапрос в качестве производной таблицы (в FROM). Результат подзапроса используется как временная таблица.

    -- Найти для каждого отдела его сотрудника с максимальной зарплатой
    SELECT d.name AS department, e.name AS employee, e.salary
    FROM departments d
    JOIN (
        SELECT department_id, MAX(salary) as max_salary
        FROM employees
        GROUP BY department_id
    ) max_sal ON d.id = max_sal.department_id
    JOIN employees e ON e.department_id = max_sal.department_id 
                      AND e.salary = max_sal.max_salary;

Ключевые соображения и опыт:

  • Производительность: Коррелированные подзапросы (которые ссылаются на столбцы внешнего запроса) могут выполняться очень медленно для больших таблиц, так как они запускаются для каждой строки внешнего запроса. В таких случаях часто более эффективна замена на JOIN.

    • Медленно (коррелированный): SELECT * FROM users u WHERE (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) > 5;
    • Быстрее (через JOIN и GROUP BY): SELECT u.* FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id HAVING COUNT(*) > 5;
  • Читаемость: Сложные многоуровневые подзапросы могут сильно ухудшать читаемость SQL. В современных СУБД часто предпочтительнее использовать CTE (Common Table Expressions, WITH clause), которые структурируют запрос и делают его более понятным.

    WITH high_value_orders AS (
        SELECT user_id, SUM(total) as order_sum
        FROM orders
        GROUP BY user_id
        HAVING SUM(total) > 10000
    )
    SELECT u.name, hvo.order_sum
    FROM users u
    JOIN high_value_orders hvo ON u.id = hvo.user_id;
  • Возможности СУБД: Современные оптимизаторы запросов (в PostgreSQL, MySQL 8+, SQL Server) умеют очень хорошо "разворачивать" и оптимизировать подзапросы, иногда преобразуя их в те же JOIN на этапе построения плана выполнения. Всегда стоит проверять план запроса (EXPLAIN) для критичных по производительности операций.

В итоге, подзапросы — мощный инструмент, но их применение требует понимания их влияния на производительность и предпочтения в пользу JOIN или CTE в сложных случаях для поддержания чистоты и скорости кода.

Ответ 18+ 🔞

Да ты послушай, что за тема интересная! Подзапросы в SQL — это, блядь, как карманный ножик у швейцара: вроде мелочь, а без него нихуя не сделать. Я их, конечно, юзал до овердохуища раз. Это когда один запрос впихнут в другой, как матрёшка, только с данными. Может вернуть одно число, список или целую таблицу — универсальная хуйня.

На что они годятся и как выглядят:

  1. Простой, как хуй с горы. Возвращает одно значение, и его можно воткнуть куда угодно.

    -- Найти всех работяг, которые получают больше, чем средняя зп по конторе
    SELECT name, salary
    FROM employees
    WHERE salary > (SELECT AVG(salary) FROM employees);
  2. Списочный, для проверки «входит или нет». Тут уже IN, EXISTS и прочая хуета в дело идёт.

    -- Найти товары, которые нихуя никогда не покупали
    SELECT id, name
    FROM products p
    WHERE NOT EXISTS (
        SELECT 1 FROM order_items oi WHERE oi.product_id = p.id
    );
  3. Табличный, чтоб временную таблицу сварганить прямо в запросе. Результат подзапроса потом джойнишь, как обычную.

    -- Найти в каждом отделе самого жирного кота по зарплате
    SELECT d.name AS department, e.name AS employee, e.salary
    FROM departments d
    JOIN (
        SELECT department_id, MAX(salary) as max_salary
        FROM employees
        GROUP BY department_id
    ) max_sal ON d.id = max_sal.department_id
    JOIN employees e ON e.department_id = max_sal.department_id 
                      AND e.salary = max_sal.max_salary;

А теперь, блядь, жизненный опыт и подводные камни:

  • Скорость, ёпта! Коррелированные подзапросы — это когда внутренний запрос тычется в поля внешнего — могут тормозить, как мартышлюшка в янтаре. Они выполняются для КАЖДОЙ строчки основного запроса. В таких случаях джойн с GROUP BY почти всегда рулит.

    • Тупит (коррелированный): SELECT * FROM users u WHERE (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) > 5;
    • Летит (джойн): SELECT u.* FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id HAVING COUNT(*) > 5;
  • Читаемость, ёб твою мать! Когда подзапросов наворочено, как говна за баней, разобраться в этой каше — терпения ноль ебать. Поэтому сейчас все умные дяди используют CTE (Common Table Expressions, через WITH). Это как навести порядок в голове: сначала назвал кусок логики, а потом его используешь. Красота!

    WITH high_value_orders AS (
        SELECT user_id, SUM(total) as order_sum
        FROM orders
        GROUP BY user_id
        HAVING SUM(total) > 10000
    )
    SELECT u.name, hvo.order_sum
    FROM users u
    JOIN high_value_orders hvo ON u.id = hvo.user_id;
  • Базы данных тоже умнеют. Современные СУБД (PostgreSQL, MySQL 8+) уже не лыком шиты — их оптимизаторы могут на лету переварить твой кривой подзапрос и превратить его в эффективный джойн. Но доверия ебать ноль — для серьёзных вещей всегда смотри план выполнения (EXPLAIN), а не молись.

Итог: Подзапросы — инструмент мощный, но, как и всё в этой жизни, требует мозгов. Не злоупотребляй, особенно коррелированными, и помни про джойны и CTE, когда запрос начинает напоминать пиздопроебибну. Чистота кода и скорость — наше всё.