Пары штатов с похожими потоками
Условие
Дана таблица state_streams со следующей структурой:
- state (VARCHAR) — название штата
- total_streams (INT) — общее количество потоков
Напишите SQL-запрос для получения пар штатов, у которых количество потоков отличается не более чем на 1000.
Требования
- Использовать CROSS JOIN или самообъединение
- Исключить дубликаты пар (например, (CA, NY) и (NY, CA) — одна и та же пара)
- Исключить пары штата с самим собой
Пример данных
| state | total_streams |
|---|---|
| CA | 50000 |
| NY | 50500 |
| TX | 45000 |
| FL | 50800 |
Ожидаемый результат
| state1 | state2 | streams1 | streams2 |
|---|---|---|---|
| CA | NY | 50000 | 50500 |
| CA | FL | 50000 | 50800 |
| NY | FL | 50500 | 50800 |
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Задача требует нахождения пар штатов с похожей популярностью потоков. Ключевой момент — исключить дубликаты пар и пары с самим собой.
Подход к решению
Логика:
- Использовать CROSS JOIN для создания всех возможных комбинаций штатов
- Отфильтровать пары, где разница потоков <= 1000
- Исключить пары штата с самим собой
- Исключить дубликаты пар (использовать упорядочение по названию)
SQL-запрос (основной вариант)
SELECT
s1.state AS state1,
s2.state AS state2,
s1.total_streams AS streams1,
s2.total_streams AS streams2,
ABS(s1.total_streams - s2.total_streams) AS streams_diff
FROM state_streams s1
CROSS JOIN state_streams s2
WHERE s1.state < s2.state -- Исключает дубликаты и пары с самим собой
AND ABS(s1.total_streams - s2.total_streams) <= 1000
ORDER BY s1.state, s2.state;
Пошаговое объяснение
1. CROSS JOIN
- Создаёт все возможные комбинации строк из двух таблиц
- Если в таблице 4 штата, CROSS JOIN создаст 4 × 4 = 16 комбинаций
2. Условие s1.state < s2.state
- Исключает пары штата с самим собой (CA < CA = FALSE)
- Исключает дубликаты пар (CA < NY = TRUE, но NY < CA = FALSE)
- Гарантирует каждую пару только один раз в алфавитном порядке
- Это критичный момент для задачи!
3. ABS(s1.total_streams - s2.total_streams) <= 1000
- ABS — абсолютное значение разницы (работает в обе стороны)
- Выбираем пары, где разница <= 1000
4. Вывод разницы потоков
- Добавляю столбец streams_diff для наглядности
Альтернативный вариант: С использованием самообъединения (Self-Join)
SELECT
s1.state,
s2.state,
s1.total_streams,
s2.total_streams
FROM state_streams s1
JOIN state_streams s2
ON s1.state < s2.state
AND ABS(s1.total_streams - s2.total_streams) <= 1000
ORDER BY s1.state, s2.state;
Этот вариант более компактен и использует JOIN вместо CROSS JOIN с WHERE.
Вариант с использованием ROW_NUMBER (для избежания дубликатов)
WITH pairs AS (
SELECT
s1.state AS state1,
s2.state AS state2,
s1.total_streams AS streams1,
s2.total_streams AS streams2,
ABS(s1.total_streams - s2.total_streams) AS diff,
ROW_NUMBER() OVER (
PARTITION BY LEAST(s1.state, s2.state), GREATEST(s1.state, s2.state)
ORDER BY s1.state
) AS rn
FROM state_streams s1
CROSS JOIN state_streams s2
WHERE s1.state != s2.state
AND ABS(s1.total_streams - s2.total_streams) <= 1000
)
SELECT
state1,
state2,
streams1,
streams2
FROM pairs
WHERE rn = 1
ORDER BY state1, state2;
Это более сложный, но универсальный подход с LEAST/GREATEST.
Пример выполнения
Исходные данные:
state | total_streams
------|---------------
CA | 50000
NY | 50500
TX | 45000
FL | 50800
После CROSS JOIN (неполный список):
s1.state | s2.state | s1.streams | s2.streams | difference
---------|----------|------------|------------|----------
CA | CA | 50000 | 50000 | 0 ❌ (пара сама с собой)
CA | NY | 50000 | 50500 | 500 ✓
CA | TX | 50000 | 45000 | 5000 ❌ (> 1000)
CA | FL | 50000 | 50800 | 800 ✓
NY | CA | 50500 | 50000 | 500 ❌ (дубликат)
NY | NY | 50500 | 50500 | 0 ❌ (пара сама с собой)
NY | TX | 50500 | 45000 | 5500 ❌ (> 1000)
NY | FL | 50500 | 50800 | 300 ✓
TX | CA | 45000 | 50000 | 5000 ❌ (дубликат)
TX | NY | 45000 | 50500 | 5500 ❌ (дубликат)
TX | TX | 45000 | 45000 | 0 ❌ (пара сама с собой)
TX | FL | 45000 | 50800 | 5800 ❌ (> 1000)
FL | CA | 50800 | 50000 | 800 ❌ (дубликат)
FL | NY | 50800 | 50500 | 300 ❌ (дубликат)
FL | TX | 50800 | 45000 | 5800 ❌ (дубликат)
FL | FL | 50800 | 50800 | 0 ❌ (пара сама с собой)
Применение условия s1.state < s2.state:
CA < CA = FALSE ❌
CA < NY = TRUE ✓
CA < TX = TRUE ✓
CA < FL = TRUE ✓
NY < CA = FALSE ❌
NY < NY = FALSE ❌
NY < TX = TRUE ✓
NY < FL = TRUE ✓
TX < CA = FALSE ❌
TX < NY = FALSE ❌
TX < TX = FALSE ❌
TX < FL = TRUE ✓
FL < CA = FALSE ❌
FL < NY = FALSE ❌
FL < TX = FALSE ❌
FL < FL = FALSE ❌
Финальный результат (разница <= 1000):
state1 | state2 | streams1 | streams2 | diff
-------|--------|----------|----------|------
CA | NY | 50000 | 50500 | 500
CA | FL | 50000 | 50800 | 800
NY | FL | 50500 | 50800 | 300
Вариант с ранжированием пар по разнице
SELECT
s1.state,
s2.state,
s1.total_streams,
s2.total_streams,
ABS(s1.total_streams - s2.total_streams) AS diff,
RANK() OVER (ORDER BY ABS(s1.total_streams - s2.total_streams) ASC) AS similarity_rank
FROM state_streams s1
JOIN state_streams s2
ON s1.state < s2.state
AND ABS(s1.total_streams - s2.total_streams) <= 1000
ORDER BY diff ASC, s1.state, s2.state;
Это показывает, какие пары наиболее похожи (меньшая разница).
Расширенный вариант с статистикой
SELECT
s1.state,
s2.state,
s1.total_streams,
s2.total_streams,
ABS(s1.total_streams - s2.total_streams) AS difference,
ROUND(ABS(s1.total_streams - s2.total_streams)::NUMERIC / LEAST(s1.total_streams, s2.total_streams) * 100, 2) AS diff_percentage,
CASE
WHEN ABS(s1.total_streams - s2.total_streams) < 100 THEN 'Very Similar'
WHEN ABS(s1.total_streams - s2.total_streams) < 500 THEN 'Similar'
WHEN ABS(s1.total_streams - s2.total_streams) < 1000 THEN 'Somewhat Similar'
ELSE 'Not Similar'
END AS similarity_level
FROM state_streams s1
JOIN state_streams s2
ON s1.state < s2.state
AND ABS(s1.total_streams - s2.total_streams) <= 1000
ORDER BY difference ASC;
Это добавляет процентную разницу и категорию сходства.
Важные моменты
✓ s1.state < s2.state — критичное условие для исключения дубликатов
- Работает для текстовых сравнений (алфавитный порядок)
- Гарантирует каждую пару только один раз
- Автоматически исключает пары штата с самим собой
✓ ABS() для абсолютного значения — работает независимо от порядка чисел
- |50000 - 50500| = |50500 - 50000| = 500
✓ CROSS JOIN vs JOIN — оба работают, но JOIN явно показывает условие
✓ Производительность — для большого количества штатов может быть медленно
- Сложность: O(n²) где n — количество штатов
- Для 50 штатов: 50 × 50 = 2500 комбинаций
✓ Индексы — рекомендуется индекс на (total_streams) для оптимизации
✓ Числовые сравнения — если нужно сравнивать по процентной разнице, используй:
WHERE ABS(s1.total_streams - s2.total_streams) <= LEAST(s1.total_streams, s2.total_streams) * 0.05 -- 5% разница