occurrences - это сколько раз инн повторяется в таблицеnum_inn - количество инн, которые встречаются occurrences раз156, 444, 777 - встречаются 1 раз, но всего их 3
283 - 2 раза, всего таких инн 1
900 - 4 раза, всего тоже 1
Я нашла как минимум 2 варианта, один из которых более лаконичный, чуть менее оптимальный, но довольно необычный в качестве зарядки для мозга.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7🤔2
SELECT
occurrences,
COUNT(1) AS num_inn
FROM (
SELECT COUNT(1) AS occurrences
FROM test
GROUP BY inn
)
GROUP BY occurrences
ORDER BY num_inn DESC, occurrences;
Я обычно пишу count(1) вместо count(*), чтобы на всякий база не пошла читать все столбцы. Но многие говорят, что это миф и они абсолютно одинаковы, так что решать вам
Также в подзапросе можно группировать по столбцу, но не включать его в селект.
from -> join -> where -> group by -> having -> select (вместе с оконками) -> order by -> limit
То есть на этапе селекта мы уже сделали все группировки и можем спокойно использовать:
SELECT DISTINCT
COUNT(inn) AS occurrences,
COUNT(COUNT(inn)) OVER (PARTITION BY COUNT(inn)) AS num_inn
FROM test
GROUP BY inn
ORDER BY num_inn DESC, occurrences;
Sort(cost=43.09..43.59)
Unique(cost=52.31..53.81)
Второй запрос тяжелее, но вам может встретиться кейс, когда первый вариант не подойдет, так что имейте в виду
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5⚡2💯1
Когда стоит вопрос: использовать
LEFT или LIKE - надо брать второе. Особенно в базах, которые поддерживают индексацию в LIKE.Какие есть варианты запроса?
SELECT * FROM accounts
WHERE LEFT(account_num, 1) = '5'
SELECT * FROM accounts
WHERE account_num LIKE '5%'
План запроса:
|--Clustered Index Scan(..., WHERE:(substring(account_num,(1),(1))=N'5'))
|--Clustered Index Seek(..., SEEK:(account_num >= N'5' AND account_num < N'6'), WHERE:(account_num like N'5%') ORDERED FORWARD)
Index Scan - вытаскиваем все строки из таблицы и ищем совпадение с подстрокой.Index Seek - сразу вытаскиваем нужную группу записей, потом фильтруем.PROFIT
Проблема в использовании функций. Поэтому, если возможно, заменяйте функции поверх полей на операторы, чтобы индексы могли нормально работать:
SELECT * FROM documents
WHERE YEAR(valid_from) = 2023 and MONTH(valid_from) = 6
--Index Scan
SELECT * FROM documents
WHERE valid_from BETWEEN '2023-06-01' AND '2023-06-30'
--Index Seek 👍
#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👏4
SQL джойны в Spark
🎹 В спарке всего 7 видов:
1) inner
2) left outer
3) right outer
4) full outer
5) cross
6) left semi
7) left anti
На первых четырех останавливаться не будем, вы их точно знаете😉
Схема такая:
- с какой табличкой джойним
- условия джойна
- тип джойна
Почти для каждого типа есть несколько обозначений, например, left, leftouter, left_outer - все равнозначны. Посмотреть на примеры можно тут
✨ Часто бывает, что столбцы называются одинаково, тут в ход идут элиасы. Так мы не привязываемся к конкретным названиям датафреймов:
CROSS JOIN
В спарк конфиге при создании сессии есть параметр, который может включать и отключать cross join:
Если отключите, то в коде вызовется исключение. В Spark3 по умолчанию True, в Spark2 - False.
LEFT SEMI
То же самое, что inner join, но возвращаются только колонки из левого датасета для сметчившихся строк.
LEFT ANTI
Возвращает колонки из левого датасета, но для НЕсметчившихся строк.
🤗 Вообще left semi я особо не использую, т.к. если уж нужно джойнить с другой табличкой, то наверняка оттуда надо что-то забрать😁 А вот left anti - удобная штука, когда надо поресерчить проблемные строки и не загромождать экран ненужными колонками
#spark
1) inner
2) left outer
3) right outer
4) full outer
5) cross
6) left semi
7) left anti
На первых четырех останавливаться не будем, вы их точно знаете
Схема такая:
- с какой табличкой джойним
- условия джойна
- тип джойна
df = df1.join(df2, F.col('id') == F.col('parent_id'), 'left')Почти для каждого типа есть несколько обозначений, например, left, leftouter, left_outer - все равнозначны. Посмотреть на примеры можно тут
df = df1.alias('df1').join(df2.alias('df2'), F.col('df1.id') == F.col('df2.id'), 'left')CROSS JOIN
В спарк конфиге при создании сессии есть параметр, который может включать и отключать cross join:
.config("spark.sql.crossJoin.enabled", True)Если отключите, то в коде вызовется исключение. В Spark3 по умолчанию True, в Spark2 - False.
LEFT SEMI
То же самое, что inner join, но возвращаются только колонки из левого датасета для сметчившихся строк.
LEFT ANTI
Возвращает колонки из левого датасета, но для НЕсметчившихся строк.
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18❤3
Data Engineer в Яндекс 360
Москва/СПБ
middle, senior
Яндекс 360 — это Почта, Диск, Телемост с видеозвонками, Документы, Мессенджер, Рассылки, Заметки, Календарь, Трекер, Вики и Формы. Каждый день этими сервисами пользуются миллионы человек и тысячи компаний.
Наши продукты, команды и, конечно, объёмы данных быстро растут. Мы ищем дата-инженера, который поможет организовать хранилище, навести порядок и вывести аналитику наших сервисов на новый уровень.
Какие задачи вас ждут
- Вести разработку ETL-процессов поставки данных
- Выявлять неэффективность в существующих процессах и оптимизировать их
- Проектировать структуру хранения данных
- Погружаться в данные из прикладных бизнес-областей и становиться в них экспертом
- Сопровождать разработанные ETL-процессы
- Участвовать в развитии платформы DWH
Мы ждём, что вы
- Уверенно владеете Python
- Уверенно владеете SQL (join, агрегация, оконные функции, оптимизация сложных запросов)
- Умеете пользоваться Git
- Знаете, как строить и оптимизировать ETL-процессы
Будет плюсом, если вы
- Разбираетесь в слоях DWH и моделях хранения данных
- Работали с большими объёмами данных
Откликнуться
Please open Telegram to view this post
VIEW IN TELEGRAM
yandex.ru
Вакансия «Data Engineer в Мессенджер (Ереван)» в Яндексе — работа в компании Яндекс для IT-специалистов
Работа в компании Яндекс для специалиста «Data Engineer в Мессенджер (Ереван)» с уровнем квалификации от «Специалист» до «Старший» — Высокая заработная плата и социальные гарантии в IT-компании России
❤6🔥2💯1
CROSS JOIN LATERAL, LEFT JOIN LATERAL. Так что с другими бд, я надеюсь, тоже несложно разобраться.В чем суть?
По факту это те же самые джойны, но не с табличкой, а с результатом функции, которая возвращает табличку.
Допустим, есть функция поиска книги по автору:
CREATE FUNCTION getBookByAuthorId(@AuthorId int)
RETURNS TABLE AS RETURN
(
SELECT * FROM book
WHERE author_id = @AuthorId
)
Используем в запросе:
SELECT a.author_name, b.book_name, b.price
FROM author a
CROSS APPLY getBookByAuthorId(a.id) b
Ненайденные строчки также заполняются NULL.
В этом случае аналогично подзапросу в селекте:
--outer apply
SELECT a.author_name, book_list
FROM author a
OUTER APPLY (
SELECT STRING_AGG(book_name, ', ') AS book_list
FROM book b
WHERE b.author_id = a.id
) sub
--подзапрос
SELECT a.author_name, (
SELECT STRING_AGG(book_name, ', ')
FROM book b
WHERE b.author_id = a.id
) AS book_list
FROM author a
--STRING_AGG соединяет строки в одну ячейку с разделителем
С агрегирующими функциями прокатит только outer apply, потому что агрегировать поверх подзапроса нельзя. А так можно:
SELECT a.author_name, count(book_id) as book_num
FROM author a
OUTER APPLY (
SELECT b.id as book_id
FROM book b
WHERE b.author_id = a.id
) sub
GROUP BY a.author_name
#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5❤2👏2
Все sql-джойны внутри превращаются в один из 4х типов:
Какой из них используется - смотрим через
df.explain()Сейчас пройдемся по каждому и посмотрим на условия срабатывания.
- условие на равенство
F.col('df1.id') == F.col('df2.id')- ключи должны быть сортируемы
Примеры несортируемых ключей: бинарный формат, сложные структуры
Что такое broadcast?
shuffle - группируются одинаковые ключи из разных экзекьюторов на одном => большие расходы на сеть. Но если у нас есть маленькая табличка (десятки мегабайт, но по умолчанию просто 10), то мы можем скопировать ее на все экзекьюторы, поджойнить там же и избежать шафла. Лимит на размер - память самого экзекьютора.- условие на равенство
- включен broadcast в настройках сессии
# 2й аргумент - размер в МБ
.config('spark.sql.autoBroadcastJoinThreshold', 100)
# так отключается broadcast
.config('spark.sql.autoBroadcastJoinThreshold', -1)
# broadcast join
df1.join(F.broadcast(df2), condition, join_type)
- условие на неравенство
- включен broadcast
NestedLoop - потому что мы итерируемся по маленькому датасету и проверяем каждую строчку
- условие на неравенство
Самый медленный, может приводить к OOM-ошибкам (Out of Memory).
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10⚡2💯2❤🔥1❤1
В общем, этот пост для ваших идей, насущных вопросов и всего, чего хотите
Please open Telegram to view this post
VIEW IN TELEGRAM
👏27🎉13❤🔥6⚡4❤3🏆2🔥1🆒1
В спарке существуют 2 вида трансформаций: узкие и широкие.
where(), withColumn(), union(). Например, чтобы отфильтровать строки, нам не нужно знать весь датасет. Мы берем одну строку, применяем условие - готово.join(), groupBy(), sort(), distinct(). Здесь же нам нужен весь датасет. Допустим, мы хотим сделать дистинкт по полю color: на первом экзекьюторе лежат red, blue, green, на втором yellow, violet, blue. Если брать отдельно каждый экзекьютор, то цвета уникальны, но если мы возьмем все, то будут дубликаты. То есть нам сначала надо одинаковые значения собрать (это и есть шафл) и только потом почистить.Аналогично работают и джойны, поэтому нужно уменьшить количество данных на стадии шафла.
Есть несколько советов:
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤1👌1💯1🏆1
Как использовать?
Диалект - MS SQL Server
SELECT *
FROM test (NOLOCK)
NOLOCK убирает блокировки, связанные с параллельным обращением к одним и тем же данным. Другое название - READ UNCOMMITTED, т.е. нам не нужно ждать, пока транзакция завершится. Но тут возникает вопрос с чтением грязных данных - мы можем работать с данными, которые уже сто раз изменились.#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
🧩Работаем с партициями
➗ Вообще партицирование/партиционирование - это метод разделения большой таблицы на маленькие кусочки для оптимизации обращения к ним. Часто партицируют по дате или по полю с небольшим конечным множеством значений. В HDFS они выглядят как подпапочки внутри одной папки:
А уже внутри этого пути лежат паркет-файлики вида part-23001-b484-8348bde71.c000.parquet
Как эффективно достать данные из партицированной таблицы?
У нас есть табличка visits в схеме web, и она партицирована по дате. Нужно вытащить максимальную дату.
Самый простой способ - написать запрос:
Но мы также можем воспользоваться преимуществами Hive и сделать так:
В результате show partitions у нас появляется одно поле partition:
В первом случае мы должны пробежаться по куче данных и агрегировать. Во втором у нас всего лишь небольшой список, так что этот метод в разы быстрее🍊
#spark
hdfs://data/web/visits/ - вся таблицаhdfs://data/web/visits/visit_date=2024-03-01 - конкретная партицияА уже внутри этого пути лежат паркет-файлики вида part-23001-b484-8348bde71.c000.parquet
Как эффективно достать данные из партицированной таблицы?
У нас есть табличка visits в схеме web, и она партицирована по дате. Нужно вытащить максимальную дату.
Самый простой способ - написать запрос:
spark.table("web.visits")
.select(max("visit_date"))
.show() Но мы также можем воспользоваться преимуществами Hive и сделать так:
spark.sql("SHOW PARTITIONS web.visits")
.select(max("partition"))
.show()В результате show partitions у нас появляется одно поле partition:
+----------+
|partition |
+----------+
|2024-03-01|
|2024-02-26|
+----------+
В первом случае мы должны пробежаться по куче данных и агрегировать. Во втором у нас всего лишь небольшой список, так что этот метод в разы быстрее
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤4⚡1
Работа с таблицами в Hive
В Hive есть два типа таблиц:
🔘 internal (managed)
🔘 external (not managed)
Managed означает, что Hive в ответе за тех, кого приручил🌹
😑 Internal таблица похожа на обычную таблицу из любой СУБД - она хранит данные и метаданные (в Hive Metastore) о них. Туда можно вставлять данные, из неё можно читать. А ещё она поддерживает кэширование (переиспользование уже посчитанного запроса в других) и, начиная с определённой версии движка, ACID-транзакции. Если удалить такую таблицу, данные тоже удалятся, тут как обычно.
Пример создания таблицы, партиционированной по дате, которая хранится в виде паркет-файлов:
🤗 А еще есть External таблица, которая хранит только метаданные о том, где лежат файлики и как их оттуда читать. Многие функции вроде ACID-транзакций и кэширования не поддерживаются. Если таблицу удалить, данные останутся лежать на дисках.
По созданию то же самое, нужно только дописать ключевое слово external:
Кстати, на днях мы пытались считать данные из таблицы через оркестратор и столкнулись с ошибкой "Your client does not appear to support insert-only tables". Оказалось, что по дефолту у нас на кластере таблицы создаются транзакционными и нужно добавить опцию:
😶 При этом эти настройки не сработали с managed табличкой, вероятно, при настройке кластера добавили параметр hive.strict.managed.tables=true, который запрещает создание управляемых и нетранзакционных таблиц. Но с external этот метод сработал!
👫 Если вы хотите узнать побольше о том, какие полезные команды используются в Hive почти каждый день, смотрите вторую часть поста https://news.1rj.ru/str/rzv_de/132
А еще можете пройтись и по другим заметкам)
#hive
В Hive есть два типа таблиц:
Managed означает, что Hive в ответе за тех, кого приручил
Пример создания таблицы, партиционированной по дате, которая хранится в виде паркет-файлов:
CREATE TABLE web.visits (
visitid int,
url string
)
PARTITIONED BY (dt string)
STORED AS PARQUET;
По созданию то же самое, нужно только дописать ключевое слово external:
CREATE EXTERNAL TABLE web.visits (...)
Кстати, на днях мы пытались считать данные из таблицы через оркестратор и столкнулись с ошибкой "Your client does not appear to support insert-only tables". Оказалось, что по дефолту у нас на кластере таблицы создаются транзакционными и нужно добавить опцию:
ALTER TABLE web.visits SET TBLPROPERTIES('transactional'='false');А еще можете пройтись и по другим заметкам)
#hive
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥7👏1
Выходные прошли, погнали снова работать)
Есть ли разница в запросах?
Опрос ниже👇
Есть ли разница в запросах?
--1
SELECT *
FROM table1 t1
JOIN table2 t2
ON t1.id = t2.id or t1.name = t2.name;
--2
SELECT *
FROM table1 t1
JOIN table2 t2
ON t1.id = t2.id
UNION ALL
SELECT *
FROM table1 t1
JOIN table2 t2
ON t1.name = t2.name;
Опрос ниже
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9
Есть ли разница в запросах? (пост выше)
Anonymous Quiz
47%
Да, первый запрос быстрее
43%
Да, второй запрос быстрее
10%
Нет, запросы по времени одинаковы
Смотрим план!
Nested Loop/Cartesian ProductHash Join/SortMergeJoinКажется, что union сильно лучше, т.к. не происходит комбинаторного взрыва. Когда я работала на MS SQL, то сталкивалась с подобными запросами с OR в условии джойна, и они подвисали конкретно. Но на медиуме есть статейки/комменты для обеих сторон.
В общем, it depends)
Смотрите планы запросов, замеряйте время, понимайте логику данных, выбирайте то, что лучше при ваших условиях
Please open Telegram to view this post
VIEW IN TELEGRAM
👨💻4👀2❤🔥1👍1
Про личный бренд
😯 Недавно у нас стартанул курс по амбассадорству "Академия амбассадоров Сбера", где я буду учиться на протяжении 6 недель, и это моя домашка)
Там есть два трека: блогер и эксперт, - оказалось, что я эксперт с блогом. Буду хорошо учиться, чтобы постить сюда еще более крутой контент💪
Планирую пробовать новые форматы (даже если некомфортно) и прокачивать себя. Как говорится, "Иди туда, где страшно"🌝
Нас ждут двухчасовые встречи с экспертами😮 Надеюсь, они помогут осознать себя в пространстве и дадут крутые наводки к публичным выступлениям на проф. конфах.
▶️ Так что когда-нибудь обязательно ждите на экранах ваших смартфонов!
Пока все!
Там есть два трека: блогер и эксперт, - оказалось, что я эксперт с блогом. Буду хорошо учиться, чтобы постить сюда еще более крутой контент
Планирую пробовать новые форматы (даже если некомфортно) и прокачивать себя. Как говорится, "Иди туда, где страшно"
Нас ждут двухчасовые встречи с экспертами
Пока все!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👏7❤5
Какие даты вернутся в запросе, если оригинальная дата в UTC?
SELECT * FROM dates WHERE toDateTime(`datetime`, 'Europe/Moscow') BETWEEN '2024-02-24' AND '2024-02-25'
SELECT * FROM dates WHERE toDateTime(`datetime`, 'Europe/Moscow') BETWEEN '2024-02-24' AND '2024-02-25'
Anonymous Poll
3%
2024-02-23 20:59:00
40%
2024-02-23 21:00:00
36%
2024-02-23 23:59:00
53%
2024-02-24 00:00:00
62%
2024-02-24 02:59:00
67%
2024-02-24 20:59:00
56%
2024-02-24 21:00:00
41%
2024-02-24 22:00:00
48%
2024-02-25 00:00:00
26%
2024-02-25 02:59:00
🔥1
Правильные варианты - со 2 по 7.
При конвертации UTC -> UTC+3 добавляется 3ч ко всем строкам, поэтому период начинается с 21:00 (UTC) = 00:00 (UTC+3) и заканчивается тоже в 21:00, потому что в between самое крайнее время - это не конец дня, а начало дня:
--это одинаковые условия
BETWEEN '2024-02-24' AND '2024-02-25'
BETWEEN '2024-02-24 00:00:00' AND '2024-02-25 00:00:00'
Зачем?
Потом я увидела, что день начинался в 21:00 предыдущего дня и заканчивался в 20:59 текущего. Так, разница в 3 часа, значит, связано с конвертацией дат в UTC - но это было не совсем так!
Я работала в DBeaver, а когда написала тот же запрос в PyCharm - даты отображались корректно, с 00:00 до 23:59.
Возможно, во всем виноваты настройки, но так и не получилось с ними разобраться. PyCharm люблю всей душой
Код потестить (ClickHouse):
CREATE TABLE dates (
`datetime` datetime('UTC')
)
ENGINE = MergeTree()
ORDER BY datetime;
INSERT INTO dates VALUES
('2024-02-23 20:59:00'), ('2024-02-23 21:00:00'), ('2024-02-23 23:59:00'), ('2024-02-24 00:00:00'), ('2024-02-24 02:59:00'), ('2024-02-24 20:59:00'), ('2024-02-24 21:00:00'), ('2024-02-24 22:00:00'), ('2024-02-25 00:00:00'), ('2024-02-25 02:59:00');
SELECT
toDateTime(`datetime`, 'Europe/Moscow'),
CASE
WHEN toDateTime(`datetime`, 'Europe/Moscow') BETWEEN '2024-02-24' AND '2024-02-25' THEN 1
ELSE 0
END AS flag
FROM dates
ORDER BY 1;
Please open Telegram to view this post
VIEW IN TELEGRAM
👏3🔥2😁1
Недавно ходила на сходку ODS (Open Data Science) и познакомилась с одним дата аналитиком. В общих чертах состоялся такой диалог:
- Чем вы занимаетесь?
- Шатаю таблички.
- И каким образом?
- Да просто select *, и все)
Так вот этот пост про do и don'ts в сфере работы с данными:
SELECT *, а не только нужные поля - вдруг они пригодятся в будущем? И никаких LIMIT - мы не хотим делать выводы на крошечной выборкеON, WHERE и т.д. - лучше сделайте побыстрее и идите отдыхатьOR, не пытайтесь заменить на IN, UNION и т.д.DISTINCT, он должен быть в каждом подзапросе - для нашей 200% уверенностиUPPER, LOWER, LEFT, RIGHT... Ну а WHERE UPPER(name) LIKE '_Mary%'- вообще песня!
_
%
Please open Telegram to view this post
VIEW IN TELEGRAM
😁29🔥4👌1
Выберите ВСЕ работающие варианты сортировки по убыванию в спарке:
Anonymous Poll
54%
df.sort(F.col('id').desc())
65%
df.orderBy(F.col('id').desc())
16%
df.sort('id'.desc())
22%
df.orderBy('id'.desc())
59%
df.sort(F.col('id'), ascending=False)
54%
df.orderBy(F.col('id'), ascending=False)
41%
df.sort('id', ascending=False)
57%
df.orderBy('id', ascending=False)
41%
df.sort(F.desc('id'))
43%
df.orderBy(F.desc('id'))
👀3🔥1
Правильные ответы - все, кроме
'id'.desc()
sort vs orderBy - что вам больше нравится, никакой разницыСпособов тоже много на ваш выбор:
F.col().desc()Через метод столбца. Мы можем навесить функцию только на объект типа колонка, на обычную строку - нет:
df.orderBy(F.col('id').desc())ascending=FalseЧерез свойство, в сортировке - строка или колонка:
df.orderBy('id', ascending=False)
df.orderBy(F.col('id'), ascending=False)F.desc()Через функцию, аргумент - строка или колонка:
df.orderBy(F.desc('id'))
df.orderBy(F.desc(F.col('id')))spark.sqlДля особо ленивых)
spark.sql('select * from my_table order by id desc')#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5❤1👌1💯1