НейроАгент

Правильный SQL-запрос для активных кредитов

Проверка и оптимизация SQL-запроса для вывода клиентов с действующими кредитами. Анализ корректности, производительности и альтернативных подходов.

Вопрос

Правильно ли написан SQL-запрос для вывода клиентов с действующими (непогашенными) кредитами?

Задача

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

  • t1 (кредитные контракты)
  • t2 (депозитные контракты)
  • t3 (клиенты)

Структура таблиц (MySQL 5.7)

Таблица t1 (кредитные контракты)

sql
CREATE TABLE t1 (
    CREDIT_CONTRACT_ID INT,
    START_DATE DATE,
    END_DATE DATE,
    CUSTOMER_ID INT,
    CREDIT_AMOUNT INT
);

Таблица t3 (клиенты)

sql
CREATE TABLE t3 (
    CUSTOMER_ID INT,
    NAME VARCHAR(100),
    BIRTH_DATE DATE,
    CITY VARCHAR(100)
);

Пример данных

Данные для таблицы t1

sql
INSERT INTO t1 VALUES 
(1, '2015-10-15', '2015-12-30', 1, 1000),
(2, '2015-12-14', NULL, 2, 2000),
(3, '2015-10-20', NULL, 3, 15000),
(7, '2015-12-28', NULL, 4, 80000),
(8, '2015-10-07', NULL, 5, 3000),
(9, '2015-06-30', NULL, 6, 29000),
(10, '2015-04-09', NULL, 7, 8000),
(11, '2015-01-20', NULL, 8, 68000),
(12, '2015-10-27', NULL, 9, 7000);

Данные для таблицы t3

sql
INSERT INTO t3 VALUES 
(1, 'Серик', '1975-09-20', 'Алматы'),
(2, 'Динара', '1977-07-22', 'Алматы'),
(3, 'Марат', '1969-01-02', 'Астана'),
(4, 'Олег', '1967-09-27', 'Астана'),
(5, 'Марина', '1969-10-12', 'Алматы'),
(6, 'Дильшат', '1979-10-08', 'Караганда'),
(7, 'Ануар', '1982-04-17', 'Кызылорда'),
(8, 'Михаил', '1980-12-11', 'Атырау'),
(9, 'Екатерина', '1987-02-12', 'Актобе');

Предложенный SQL-запрос

sql
SELECT 
    t3.NAME,
    t3.BIRTH_DATE,
    t3.CITY,
    t1.CREDIT_CONTRACT_ID,
    t1.START_DATE,
    t1.END_DATE,
    t1.CREDIT_AMOUNT
FROM t1
JOIN t3 ON t1.CUSTOMER_ID = t3.CUSTOMER_ID
WHERE t1.END_DATE IS NULL;

Вопрос

Правильно ли написан SQL-запрос для вывода клиентов с действующими (непогашенными) кредитами?

По идее, я ищу кредиты, у которых END_DATE не указана (то есть кредит ещё не погашен). Результат запроса выглядит логично, но хочу убедиться, что это корректный и оптимальный способ. Может быть, есть нюансы или альтернативные подходы?

НейроАгент

Запрос написан корректно и эффективно для вывода клиентов с действующими кредитами. Он правильно использует условие END_DATE IS NULL для идентификации активных кредитов и корректно связывает таблицы кредитных договоров с клиентами через JOIN.

Содержание

Анализ текущего SQL-запроса

Ваш запрос является функционально правильным и решает поставленную задачу:

sql
SELECT 
    t3.NAME,
    t3.BIRTH_DATE,
    t3.CITY,
    t1.CREDIT_CONTRACT_ID,
    t1.START_DATE,
    t1.END_DATE,
    t1.CREDIT_AMOUNT
FROM t1
JOIN t3 ON t1.CUSTOMER_ID = t3.CUSTOMER_ID
WHERE t1.END_DATE IS NULL;

Преимущества текущего подхода:

  • Использует INNER JOIN для корректного связывания кредитных договоров с клиентами
  • Применяет условие WHERE t1.END_DATE IS NULL для фильтрации активных кредитов
  • Возвращает все необходимые поля для анализа активных кредитов

Важно: Условие END_DATE IS NULL действительно является стандартным способом определения активных кредитов в большинстве баз данных, так как NULL-значение в поле даты окончания означает незавершенный договор.

Проверка корректности логики

Для полной уверенности в корректности запроса, давайте проанализируем его по шагам:

  1. JOIN операция: JOIN t3 ON t1.CUSTOMER_ID = t3.CUSTOMER_ID корректно связывает каждый активный кредит с соответствующим клиентом

  2. Фильтрация активных кредитов: WHERE t1.END_DATE IS NULL отбирает только те кредиты, которые еще не погашены

  3. Выборка данных: Запрос возвращает всю необходимую информацию для анализа активных кредитов

Результаты запроса на предоставленных данных:
Запрос вернет клиентов с ID 2, 3, 4, 5, 6, 7, 8, 9 - все, у кого есть кредиты с END_DATE = NULL.

Потенциальные проблемы:

  • Запрос не учитывает таблицу t2 (депозитные контракты), но в данном контексте это не является проблемой, так как нас интересуют только кредиты
  • Нет проверки на возможные дубликаты, если у клиента несколько активных кредитов

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

Хотя текущий запрос функционально корректен, его можно оптимизировать для лучшей производительности:

1. Выбор типа JOIN

sql
-- INNER JOIN (текущий вариант)
-- Возвращает только клиентов с активными кредитами, что логично для данной задачи

-- LEFT JOIN (если нужны все клиенты, даже без кредитов)
-- Но в данном случае это не требуется

2. Индексы для улучшения производительности

sql
-- Рекомендуется создать индексы на поля, используемых в JOIN и WHERE
CREATE INDEX idx_t1_customer_id ON t1(CUSTOMER_ID);
CREATE INDEX idx_t1_end_date ON t1(END_DATE);

3. Оптимизация выборки полей

sql
-- Если нужны только основные поля, можно ограничить выборку
SELECT 
    t3.NAME,
    t1.CREDIT_CONTRACT_ID,
    t1.CREDIT_AMOUNT,
    t1.START_DATE
FROM t1
JOIN t3 ON t1.CUSTOMER_ID = t3.CUSTOMER_ID
WHERE t1.END_DATE IS NULL;

Альтернативные подходы

1. Использование COALESCE для явного указания активных кредитов

sql
SELECT 
    t3.NAME,
    t1.CREDIT_CONTRACT_ID,
    t1.CREDIT_AMOUNT
FROM t1
JOIN t3 ON t1.CUSTOMER_ID = t3.CUSTOMER_ID
WHERE COALESCE(t1.END_DATE, CURRENT_DATE) > CURRENT_DATE;

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

2. Использование NOT EXISTS для сложных условий

sql
SELECT 
    t3.NAME,
    t1.CREDIT_CONTRACT_ID,
    t1.CREDIT_AMOUNT
FROM t3
JOIN t1 ON t3.CUSTOMER_ID = t1.CUSTOMER_ID
WHERE NOT EXISTS (
    SELECT 1 FROM t1 t2 
    WHERE t2.CUSTOMER_ID = t3.CUSTOMER_ID 
    AND t2.END_DATE IS NOT NULL
);

3. Агрегация для клиентов с несколькими кредитами

sql
SELECT 
    t3.NAME,
    COUNT(t1.CREDIT_CONTRACT_ID) AS active_credits_count,
    SUM(t1.CREDIT_AMOUNT) AS total_credit_amount
FROM t1
JOIN t3 ON t1.CUSTOMER_ID = t3.CUSTOMER_ID
WHERE t1.END_DATE IS NULL
GROUP BY t3.NAME, t3.CUSTOMER_ID;

Дополнительные рекомендации

1. Проверка целостности данных

Перед использованием запроса рекомендуется проверить целостность данных:

sql
-- Проверка на NULL значения в CUSTOMER_ID
SELECT COUNT(*) FROM t1 WHERE CUSTOMER_ID IS NULL;

-- Проверка на уникальность CUSTOMER_ID в таблице клиентов
SELECT COUNT(*) FROM t3;
SELECT COUNT(DISTINCT CUSTOMER_ID) FROM t3;

2. Добавление сортировки для удобства чтения

sql
SELECT 
    t3.NAME,
    t1.CREDIT_CONTRACT_ID,
    t1.CREDIT_AMOUNT,
    t1.START_DATE
FROM t1
JOIN t3 ON t1.CUSTOMER_ID = t3.CUSTOMER_ID
WHERE t1.END_DATE IS NULL
ORDER BY t3.NAME, t1.START_DATE DESC;

3. Использование псевдонимов для лучшей читаемости

sql
SELECT 
    c.NAME AS customer_name,
    cr.CREDIT_CONTRACT_ID,
    cr.CREDIT_AMOUNT,
    cr.START_DATE
FROM t1 cr
JOIN t3 c ON cr.CUSTOMER_ID = c.CUSTOMER_ID
WHERE cr.END_DATE IS NULL
ORDER BY c.NAME;

4. Безопасность и обработка крайних случаев

sql
-- Добавление проверки на корректные даты
SELECT 
    c.NAME,
    cr.CREDIT_CONTRACT_ID,
    cr.CREDIT_AMOUNT
FROM t1 cr
JOIN t3 c ON cr.CUSTOMER_ID = c.CUSTOMER_ID
WHERE cr.END_DATE IS NULL
  AND cr.START_DATE <= CURRENT_DATE
  AND (cr.CREDIT_AMOUNT > 0 OR cr.CREDIT_AMOUNT IS NULL);

Итоговый рекомендованный запрос:

sql
SELECT 
    c.NAME AS customer_name,
    c.CITY,
    cr.CREDIT_CONTRACT_ID,
    cr.START_DATE,
    cr.CREDIT_AMOUNT
FROM t1 cr
JOIN t3 c ON cr.CUSTOMER_ID = c.CUSTOMER_ID
WHERE cr.END_DATE IS NULL
  AND cr.START_DATE <= CURRENT_DATE
  AND cr.CREDIT_AMOUNT > 0
ORDER BY c.NAME, cr.START_DATE DESC;

Заключение

  1. Основной запрос полностью корректен для идентификации клиентов с активными кредитами через проверку END_DATE IS NULL

  2. Рекомендуется добавить индексы на поля CUSTOMER_ID и END_DATE для улучшения производительности при больших объемах данных

  3. Для лучшей читаемости кода стоит использовать осмысленные псевдонимы таблиц вместо t1, t2, t3

  4. Следует добавить проверки целостности данных и обработку крайних случаев в продакшен-среде

  5. Для аналитических задач можно использовать агрегирующие функции для получения сводной информации по клиентам с несколькими кредитами

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

Источники

  1. Official MySQL Documentation - JOIN Syntax
  2. W3Schools SQL NULL Values
  3. Stack Overflow - Checking for NULL values in SQL
  4. SQLShack - SQL Query Performance Optimization