Правильно ли написан SQL-запрос для вывода клиентов с действующими (непогашенными) кредитами?
Задача
Необходимо вывести список клиентов с действующими (непогашенными) кредитами, используя три таблицы:
- t1 (кредитные контракты)
- t2 (депозитные контракты)
- t3 (клиенты)
Структура таблиц (MySQL 5.7)
Таблица t1 (кредитные контракты)
CREATE TABLE t1 (
CREDIT_CONTRACT_ID INT,
START_DATE DATE,
END_DATE DATE,
CUSTOMER_ID INT,
CREDIT_AMOUNT INT
);
Таблица t3 (клиенты)
CREATE TABLE t3 (
CUSTOMER_ID INT,
NAME VARCHAR(100),
BIRTH_DATE DATE,
CITY VARCHAR(100)
);
Пример данных
Данные для таблицы t1
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
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-запрос
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-значение в поле даты окончания означает незавершенный договор.
Проверка корректности логики
Для полной уверенности в корректности запроса, давайте проанализируем его по шагам:
-
JOIN операция:
JOIN t3 ON t1.CUSTOMER_ID = t3.CUSTOMER_IDкорректно связывает каждый активный кредит с соответствующим клиентом -
Фильтрация активных кредитов:
WHERE t1.END_DATE IS NULLотбирает только те кредиты, которые еще не погашены -
Выборка данных: Запрос возвращает всю необходимую информацию для анализа активных кредитов
Результаты запроса на предоставленных данных:
Запрос вернет клиентов с ID 2, 3, 4, 5, 6, 7, 8, 9 - все, у кого есть кредиты с END_DATE = NULL.
Потенциальные проблемы:
- Запрос не учитывает таблицу
t2(депозитные контракты), но в данном контексте это не является проблемой, так как нас интересуют только кредиты - Нет проверки на возможные дубликаты, если у клиента несколько активных кредитов
Оптимизация производительности
Хотя текущий запрос функционально корректен, его можно оптимизировать для лучшей производительности:
1. Выбор типа JOIN
-- INNER JOIN (текущий вариант)
-- Возвращает только клиентов с активными кредитами, что логично для данной задачи
-- LEFT JOIN (если нужны все клиенты, даже без кредитов)
-- Но в данном случае это не требуется
2. Индексы для улучшения производительности
-- Рекомендуется создать индексы на поля, используемых в JOIN и WHERE
CREATE INDEX idx_t1_customer_id ON t1(CUSTOMER_ID);
CREATE INDEX idx_t1_end_date ON t1(END_DATE);
3. Оптимизация выборки полей
-- Если нужны только основные поля, можно ограничить выборку
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 для явного указания активных кредитов
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 для сложных условий
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. Агрегация для клиентов с несколькими кредитами
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. Проверка целостности данных
Перед использованием запроса рекомендуется проверить целостность данных:
-- Проверка на 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. Добавление сортировки для удобства чтения
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. Использование псевдонимов для лучшей читаемости
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. Безопасность и обработка крайних случаев
-- Добавление проверки на корректные даты
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);
Итоговый рекомендованный запрос:
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;
Заключение
-
Основной запрос полностью корректен для идентификации клиентов с активными кредитами через проверку
END_DATE IS NULL -
Рекомендуется добавить индексы на поля
CUSTOMER_IDиEND_DATEдля улучшения производительности при больших объемах данных -
Для лучшей читаемости кода стоит использовать осмысленные псевдонимы таблиц вместо
t1,t2,t3 -
Следует добавить проверки целостности данных и обработку крайних случаев в продакшен-среде
-
Для аналитических задач можно использовать агрегирующие функции для получения сводной информации по клиентам с несколькими кредитами
Ваш первоначальный подход является отличной отправной точкой, и предложенные оптимизации помогут сделать запрос более надежным и производительным.