HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#статьи</a></p>
1 <p><a>#статьи</a></p>
2 <ul><li>15 мар 2021</li>
2 <ul><li>15 мар 2021</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><p>Выбираем, какие фильмы посмотреть, с помощью соединения данных в SQL.</p>
4 </ul><p>Выбираем, какие фильмы посмотреть, с помощью соединения данных в SQL.</p>
5 <p>скриншот из игры team fortress 2 / valve</p>
5 <p>скриншот из игры team fortress 2 / valve</p>
6 <p>Фулстек-разработчик. Любимый стек: Java + Angular, но в хорошей компании готова писать хоть на языке Ада.</p>
6 <p>Фулстек-разработчик. Любимый стек: Java + Angular, но в хорошей компании готова писать хоть на языке Ада.</p>
7 <p>Опять эта проблема - выбрать кино на вечер. Благодаря стриминговым сервисам доступны едва ли не все фильмы мира: это бесконечное полотно с постерами и фильтры, фильтры, фильтры…</p>
7 <p>Опять эта проблема - выбрать кино на вечер. Благодаря стриминговым сервисам доступны едва ли не все фильмы мира: это бесконечное полотно с постерами и фильтры, фильтры, фильтры…</p>
8 Кусок бесконечного полотна фильмов<p><strong>МОЗГ: Поставлю-ка я фильтр по стране: пусть будет</strong><strong>Дания, и добавлю ограничение по жанру -</strong><strong>триллер… Ну вот - другое дело, относительно небольшой список.</strong></p>
8 Кусок бесконечного полотна фильмов<p><strong>МОЗГ: Поставлю-ка я фильтр по стране: пусть будет</strong><strong>Дания, и добавлю ограничение по жанру -</strong><strong>триллер… Ну вот - другое дело, относительно небольшой список.</strong></p>
9 <p>- Мозг, а знаешь почему? Да потому что здесь только фильмы, которые сняты в Дании И помечены как триллеры.</p>
9 <p>- Мозг, а знаешь почему? Да потому что здесь только фильмы, которые сняты в Дании И помечены как триллеры.</p>
10 <p><strong> - Но если мне нужно не </strong><strong>И</strong><strong>, а </strong><strong>ИЛИ</strong><strong>? Или я хочу выбирать из датских фильмов, но только</strong><strong>НЕ</strong><strong>триллеры? Как выставить такой фильтр?</strong></p>
10 <p><strong> - Но если мне нужно не </strong><strong>И</strong><strong>, а </strong><strong>ИЛИ</strong><strong>? Или я хочу выбирать из датских фильмов, но только</strong><strong>НЕ</strong><strong>триллеры? Как выставить такой фильтр?</strong></p>
11 <p>- Да не знаю я, как задать такие критерии в этом сервисе. Вот если бы можно было писать на SQL - тут бы решение нашлось для любой комбинации признаков.</p>
11 <p>- Да не знаю я, как задать такие критерии в этом сервисе. Вот если бы можно было писать на SQL - тут бы решение нашлось для любой комбинации признаков.</p>
12 <p><strong>- Пруфы будут?</strong></p>
12 <p><strong>- Пруфы будут?</strong></p>
13 <p>- Легко! Ещё и картинки будут. У меня и база фильмов уже спарсена - тренируйся не хочу.</p>
13 <p>- Легко! Ещё и картинки будут. У меня и база фильмов уже спарсена - тренируйся не хочу.</p>
14 <p>Назовём множество датских фильмов - D, а множество триллеров - T. У каждого фильма будет уникальный номер, он же ключ. Раз ключ - пусть зовётся Key.</p>
14 <p>Назовём множество датских фильмов - D, а множество триллеров - T. У каждого фильма будет уникальный номер, он же ключ. Раз ключ - пусть зовётся Key.</p>
15 <p>Заодно вспомним, как на SQL пишется простой запрос для связывания данных из двух таблиц:</p>
15 <p>Заодно вспомним, как на SQL пишется простой запрос для связывания данных из двух таблиц:</p>
16 SELECT * (или список полей) FROM D JOIN T ON D.Key=T.Key WHERE условие отбора<p>Если не уточнить тип соединения (JOIN), то по умолчанию применяется INNER JOIN - как раз тот вариант, который сработал в нашем кинофильтре. Это он выбирает и триллеры, и датские фильмы одновременно.</p>
16 SELECT * (или список полей) FROM D JOIN T ON D.Key=T.Key WHERE условие отбора<p>Если не уточнить тип соединения (JOIN), то по умолчанию применяется INNER JOIN - как раз тот вариант, который сработал в нашем кинофильтре. Это он выбирает и триллеры, и датские фильмы одновременно.</p>
17 <p>А вот и обещанная картинка:</p>
17 <p>А вот и обещанная картинка:</p>
18 Никаких компромиссов - исключительно датские триллеры<p>При<strong>INNER JOIN</strong>(внешнее соединение) выбираются только совпадающие по условию объединения данные из обеих таблиц. Порядок таблиц в запросе не важен.</p>
18 Никаких компромиссов - исключительно датские триллеры<p>При<strong>INNER JOIN</strong>(внешнее соединение) выбираются только совпадающие по условию объединения данные из обеих таблиц. Порядок таблиц в запросе не важен.</p>
19 <p>Подойдёт, если:</p>
19 <p>Подойдёт, если:</p>
20 <ul><li>я хочу выбрать датские фильмы;</li>
20 <ul><li>я хочу выбрать датские фильмы;</li>
21 <li>и согласна, что среди них могут быть триллеры;</li>
21 <li>и согласна, что среди них могут быть триллеры;</li>
22 <li>но мне не интересны триллеры производства других стран, только датские.</li>
22 <li>но мне не интересны триллеры производства других стран, только датские.</li>
23 </ul><p>Вот так будет выглядеть SQL-запрос:</p>
23 </ul><p>Вот так будет выглядеть SQL-запрос:</p>
24 SELECT * FROM D LEFT JOIN T ON D.Key=T.Key<p>И подобающая случаю диаграмма:</p>
24 SELECT * FROM D LEFT JOIN T ON D.Key=T.Key<p>И подобающая случаю диаграмма:</p>
25 Датские любых жанров, а из триллеров только датские<p><strong>LEFT JOIN</strong>(левое внешнее объединение) - то же самое, что<strong>LEFT OUTER JOIN</strong>.</p>
25 Датские любых жанров, а из триллеров только датские<p><strong>LEFT JOIN</strong>(левое внешнее объединение) - то же самое, что<strong>LEFT OUTER JOIN</strong>.</p>
26 <p>В результат попадают совпадающие по ключу данные обеих таблиц и все записи из <strong>левой</strong>таблицы, для которых не нашлось пары в правой.</p>
26 <p>В результат попадают совпадающие по ключу данные обеих таблиц и все записи из <strong>левой</strong>таблицы, для которых не нашлось пары в правой.</p>
27 <p><strong>- А что, если я вообще не фанат триллеров, но датские фильмы мне интересны?</strong></p>
27 <p><strong>- А что, если я вообще не фанат триллеров, но датские фильмы мне интересны?</strong></p>
28 <p>- Тогда к скрипту выше нужно дописать одно условие:</p>
28 <p>- Тогда к скрипту выше нужно дописать одно условие:</p>
29 SELECT * FROM D LEFT JOIN T ON D.Key=T.Key WHERE T.Key IS NULL<p>Дословно T.Key IS NULL означает, что нужно включить в результат только записи, в которых значение ключа для записей из множества триллеров пусто.</p>
29 SELECT * FROM D LEFT JOIN T ON D.Key=T.Key WHERE T.Key IS NULL<p>Дословно T.Key IS NULL означает, что нужно включить в результат только записи, в которых значение ключа для записей из множества триллеров пусто.</p>
30 <p><strong> - Как это пусто? Ведь у фильма не может быть пустой номер!</strong></p>
30 <p><strong> - Как это пусто? Ведь у фильма не может быть пустой номер!</strong></p>
31 <p>- Верно. Думай об этом не как о едином множестве фильмов, а как о парах фильмов из двух групп. Мы берём один фильм из первой группы (датские) и ищем во второй группе (триллеров) для него пару - фильм с таким же номером.</p>
31 <p>- Верно. Думай об этом не как о едином множестве фильмов, а как о парах фильмов из двух групп. Мы берём один фильм из первой группы (датские) и ищем во второй группе (триллеров) для него пару - фильм с таким же номером.</p>
32 <p>Если пара найдётся (значит, попался датский триллер) - считаем, что T.Key не пустой, иначе он как раз и будет IS NULL.</p>
32 <p>Если пара найдётся (значит, попался датский триллер) - считаем, что T.Key не пустой, иначе он как раз и будет IS NULL.</p>
33 <p>Диаграмма теперь выглядит так:</p>
33 <p>Диаграмма теперь выглядит так:</p>
34 Датские - все, кроме триллеров. Триллеры полностью исключаем<p>Если день не задался и смотреть что-то доброе и вечное настроения нет, можно установить фильтр для отбора только триллеров. И пусть даже среди них будут датские, но вот другие категории датских фильмов рассматривать не будем.</p>
34 Датские - все, кроме триллеров. Триллеры полностью исключаем<p>Если день не задался и смотреть что-то доброе и вечное настроения нет, можно установить фильтр для отбора только триллеров. И пусть даже среди них будут датские, но вот другие категории датских фильмов рассматривать не будем.</p>
35 SELECT * FROM D RIGHT JOIN T ON D.Key=T.Key<p>Вот как это выглядит на диаграмме:</p>
35 SELECT * FROM D RIGHT JOIN T ON D.Key=T.Key<p>Вот как это выглядит на диаграмме:</p>
36 Триллеры любых стран, а из датских фильмов - только триллеры<p><strong> - Подожди, ну не настолько же всё плохо - давай хотя бы датские триллеры исключим. Мне кажется, я даже догадываюсь, как это сделать:</strong></p>
36 Триллеры любых стран, а из датских фильмов - только триллеры<p><strong> - Подожди, ну не настолько же всё плохо - давай хотя бы датские триллеры исключим. Мне кажется, я даже догадываюсь, как это сделать:</strong></p>
37 SELECT * FROM D RIGHT JOIN T ON D.Key=T.Key WHERE D.Key IS NULL<p>- Совершенно верно! Наверняка и диаграмма для этого случая тебя не удивит:</p>
37 SELECT * FROM D RIGHT JOIN T ON D.Key=T.Key WHERE D.Key IS NULL<p>- Совершенно верно! Наверняка и диаграмма для этого случая тебя не удивит:</p>
38 Триллеры, но только не датские. Датских фильмов вообще не нужно.<p><strong>RIGHT JOIN</strong>(правое внешнее соединение) - то же самое, что<strong>RIGHT OUTER JOIN</strong>.</p>
38 Триллеры, но только не датские. Датских фильмов вообще не нужно.<p><strong>RIGHT JOIN</strong>(правое внешнее соединение) - то же самое, что<strong>RIGHT OUTER JOIN</strong>.</p>
39 <p>В результат объединения попадают совпадающие по ключу записи обеих таблиц и все данные из <strong>правой</strong>таблицы, для которых не нашлось пары в левой.</p>
39 <p>В результат объединения попадают совпадающие по ключу записи обеих таблиц и все данные из <strong>правой</strong>таблицы, для которых не нашлось пары в левой.</p>
40 <p><strong>- Что, если фильм нам подойдёт, когда он</strong><strong>ИЛИ датский,</strong><strong>ИЛИ триллер?</strong></p>
40 <p><strong>- Что, если фильм нам подойдёт, когда он</strong><strong>ИЛИ датский,</strong><strong>ИЛИ триллер?</strong></p>
41 <p>- Тогда и пригодился бы новый тип JOIN:</p>
41 <p>- Тогда и пригодился бы новый тип JOIN:</p>
42 SELECT * FROM D FULL OUTER JOIN T ON D.Key=T.Key<p>И вот такая "цельная" у него диаграмма:</p>
42 SELECT * FROM D FULL OUTER JOIN T ON D.Key=T.Key<p>И вот такая "цельная" у него диаграмма:</p>
43 Фильм может быть любым, если он датский или триллер<p><strong>FULL JOIN</strong>(полное внешнее соединение) - то же самое, что<strong>FULL OUTER JOIN</strong>.</p>
43 Фильм может быть любым, если он датский или триллер<p><strong>FULL JOIN</strong>(полное внешнее соединение) - то же самое, что<strong>FULL OUTER JOIN</strong>.</p>
44 <p>В результат объединения попадают совпадающие по ключу записи обеих таблиц и все строки из этих двух таблиц, для которых пар не нашлось. Порядок таблиц в запросе не важен.</p>
44 <p>В результат объединения попадают совпадающие по ключу записи обеих таблиц и все строки из этих двух таблиц, для которых пар не нашлось. Порядок таблиц в запросе не важен.</p>
45 <p><strong>- А если я хочу</strong><strong>ИЛИ датский,</strong><strong>ИЛИ триллер, но не одновременно эти два признака, так можно?</strong></p>
45 <p><strong>- А если я хочу</strong><strong>ИЛИ датский,</strong><strong>ИЛИ триллер, но не одновременно эти два признака, так можно?</strong></p>
46 <p>- Да, можно и так. Здесь снова пригодится проверка на NULL.</p>
46 <p>- Да, можно и так. Здесь снова пригодится проверка на NULL.</p>
47 SELECT * FROM D FULL OUTER JOIN T ON D.Key=T.Key WHERE D.Key IS NULL OR T.Key IS NULL<p>И общий для триллеров и датских фильмов сектор на диаграмме останется незакрашенным:</p>
47 SELECT * FROM D FULL OUTER JOIN T ON D.Key=T.Key WHERE D.Key IS NULL OR T.Key IS NULL<p>И общий для триллеров и датских фильмов сектор на диаграмме останется незакрашенным:</p>
48 Можно датские, можно триллеры, но исключаем датские триллеры<p><strong> - Что ж, похоже, мы перебрали все возможные комбинации для связывания двух множеств.</strong></p>
48 Можно датские, можно триллеры, но исключаем датские триллеры<p><strong> - Что ж, похоже, мы перебрали все возможные комбинации для связывания двух множеств.</strong></p>
49 <p>- А вот и нет. Есть ещё один, особенный джойн.</p>
49 <p>- А вот и нет. Есть ещё один, особенный джойн.</p>
50 <p>У него и персональное название есть -<strong>декартово произведение</strong>. Для двух множеств в результате CROSS JOIN получаются все возможные пары, в каждой из которых будет представитель одного и второго множества.</p>
50 <p>У него и персональное название есть -<strong>декартово произведение</strong>. Для двух множеств в результате CROSS JOIN получаются все возможные пары, в каждой из которых будет представитель одного и второго множества.</p>
51 SELECT * FROM D CROSS JOIN T<p><strong> - Стоп! А где же тут соединение по ключу:</strong><strong>ON D.Key=T.Key?</strong></p>
51 SELECT * FROM D CROSS JOIN T<p><strong> - Стоп! А где же тут соединение по ключу:</strong><strong>ON D.Key=T.Key?</strong></p>
52 <p>- В том-то и дело, что мы составляем пары, не обращая внимания на ключи, просто каждый элемент первой группы сопоставляем с каждым из второй.</p>
52 <p>- В том-то и дело, что мы составляем пары, не обращая внимания на ключи, просто каждый элемент первой группы сопоставляем с каждым из второй.</p>
53 <p><strong>- Звучит как-то сложно. Зачем вообще могут понадобиться такие пары? Для фильмов это бессмыслица какая-то получится.</strong></p>
53 <p><strong>- Звучит как-то сложно. Зачем вообще могут понадобиться такие пары? Для фильмов это бессмыслица какая-то получится.</strong></p>
54 <p>- Пожалуй. Давай возьмём пример ближе к жизни. Предположим, есть магазин одежды, и мы хотим составить для него таблицу размеров одежды, но с учётом её цвета. То есть нужны все возможные комбинации размер + цвет. Это и достигается с помощью CROSS JOIN.</p>
54 <p>- Пожалуй. Давай возьмём пример ближе к жизни. Предположим, есть магазин одежды, и мы хотим составить для него таблицу размеров одежды, но с учётом её цвета. То есть нужны все возможные комбинации размер + цвет. Это и достигается с помощью CROSS JOIN.</p>
55 <p>А схематично изобразить это можно вот так:</p>
55 <p>А схематично изобразить это можно вот так:</p>
56 Декартово произведение множеств<p><strong>CROSS JOIN</strong>(перекрёстное объединение) возвращает декартово произведение: все возможные комбинации соединения записей из первой и второй таблиц.</p>
56 Декартово произведение множеств<p><strong>CROSS JOIN</strong>(перекрёстное объединение) возвращает декартово произведение: все возможные комбинации соединения записей из первой и второй таблиц.</p>
57 <p><strong> - Ок, с джойнами теперь всё ясно. Остался только один вопрос.</strong></p>
57 <p><strong> - Ок, с джойнами теперь всё ясно. Остался только один вопрос.</strong></p>
58 <p>- И какой же?</p>
58 <p>- И какой же?</p>
59 <p><strong> - Смотреть-то что будем? 😜</strong></p>
59 <p><strong> - Смотреть-то что будем? 😜</strong></p>
60 <p>Data Science с нуля: пробуем профессии на практике за 5 дней</p>
60 <p>Data Science с нуля: пробуем профессии на практике за 5 дней</p>
61 <p>Вы разберётесь в трёх главных направлениях data science: машинном обучении, разработке на Python и визуализации данных. Решите, какая сфера вам ближе, и выполните 4 реальные задачи с данными.</p>
61 <p>Вы разберётесь в трёх главных направлениях data science: машинном обучении, разработке на Python и визуализации данных. Решите, какая сфера вам ближе, и выполните 4 реальные задачи с данными.</p>
62 <p><a>Пройти бесплатно</a></p>
62 <p><a>Пройти бесплатно</a></p>
63 <a><b>Попробуйте data science на бесплатном курсе</b>Пройдите курс по data science и изучите 3 направления в работе с данными. Решите, в какой сфере хотите развиваться дальше, и получите ценные подарки. Пройти курс →</a>
63 <a><b>Попробуйте data science на бесплатном курсе</b>Пройдите курс по data science и изучите 3 направления в работе с данными. Решите, в какой сфере хотите развиваться дальше, и получите ценные подарки. Пройти курс →</a>