Кстати, зарплатные вилки можно смотреть тут:
https://getmatch.ru/salaries - российский рынок ИТ-направлений
https://www.reddit.com/r/dataengineering/comments/188grde/quarterly_salary_discussion_dec_2023/ - а тут дискуссии на мировом рынке
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤1❤🔥1
Сейчас я работаю мидл дата инженером в Сбере в команде очень крутых коллег, всем респект!🔥
🧑🎓 Первые пару недель было реально тяжело влиться в процессы, т.к. казалось, что от меня ожидают гораздо большего, чем я могу дать. Каждый день я была в состоянии «ничего не понятно😩 ». Поэтому я после и до работы смотрела нужные видосики с karpov courses и подтягивала теоретическую базу. Видосами со мной поделился один хороший человек, поэтому старайтесь наращивать базу полезных контактов)
📺 Что мы делаем?
У нас есть несколько уровней организации: команда -> кластер -> трайб. И вот наш трайб - про маркетинг. Мои коллеги придумывают рекламу, а мы потом смотрим, как она работает.
💠 Что я делаю?
Кручу таблички, большие таблички на связке Hadoop+Spark в Jupyter Notebook👩💻
Делаю витринки с данными, которые потом мои коллеги-аналитики используют для бизнес-инсайтов✨
Строю пайплайны обработки на dbt+Airflow🐍
По ходу дела пришлось разобраться в Scala, Clickhouse и Superset, чтобы оптимизировать код, удобно складывать результаты расчетов и строить красивые чартики📊
А недавно я ходила на сходку питонистов, где со мной поделились полезной инфой по митапам (офлайн и онлайн):
https://news.1rj.ru/str/ict2go
https://news.1rj.ru/str/ITMeeting
https://news.1rj.ru/str/meetupochnaya
У нас есть несколько уровней организации: команда -> кластер -> трайб. И вот наш трайб - про маркетинг. Мои коллеги придумывают рекламу, а мы потом смотрим, как она работает.
Кручу таблички, большие таблички на связке Hadoop+Spark в Jupyter Notebook
Делаю витринки с данными, которые потом мои коллеги-аналитики используют для бизнес-инсайтов✨
Строю пайплайны обработки на dbt+Airflow
По ходу дела пришлось разобраться в Scala, Clickhouse и Superset, чтобы оптимизировать код, удобно складывать результаты расчетов и строить красивые чартики
А недавно я ходила на сходку питонистов, где со мной поделились полезной инфой по митапам (офлайн и онлайн):
https://news.1rj.ru/str/ict2go
https://news.1rj.ru/str/ITMeeting
https://news.1rj.ru/str/meetupochnaya
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12🔥8❤🔥2💯1
Конечно, никто не будет копаться в каждом проекте, но уже за пару минут можно узнать, какие инструменты вы используете и как давно этим занимаетесь. А в пет-проектах можно потрогать новые тулзы, чтобы потом добавить эти скиллы в резюме и рассказывать про них на собесах
1. Начала с парсинга html-страниц с историей игр
2. Потом узнала, что есть апишка, и начала складывать данные в json-ы
3. Позже настроила загрузку сразу в бд
4. Потом захотелось искать игры по определенным критериям и даже создать свой язык запросов
Чтобы делиться инсайтами с другими игроками. В сообществе были программисты и математики, которые предлагали разные алгоритмы расчета, от реализации которых я кайфовала.
Я настолько заинтересовалась в предметной области, что изучила новые штуки: SQLAlchemy (ORM для питона), Neo4j (графовая бд), как читать данные из api гитхаба или как деплоить сайты в GitHub Pages. Сверху можно докрутить еще Airflow, BI для полноты картины. Проект доступен тут.
Kaggle
UK
EU
Azure
Bigquery
Тематические
Песни
Разное
Поиск по датасетам
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14💯5👻3❤2
Работа дата инженером – одна из путевок в жизнь
🔍 Когда я искала в первый раз, было супер сложно. Но как только я поставила в hh «Дата инженер | Data Engineer», сразу стали прилетать приглашения. Кстати, полезно переводить название позиции, т.к. hr могут искать на разных языках.
Я думала, вот поставлю новую должность, тыкну «не ищу работу», и все посмотрят: «Ага, только устроилась, писать не будем». Но резюме всплыло наверх, и меня начали хантить уже через 3 дня после трудоустройства😁 Естественно, я всем отказывала, какой смысл собеситься на первой неделе работы?🙂 Но все это говорит о востребованности профессии❕
📝 Вот небольшой список компаний, которые набирают дата инженеров:
- Билайн
- X5 Group
- Центр финансовых технологий
- Лига цифровой экономики
- ITFB Group
- Иннотех
- Mediascope
На некоторые собесы я ходила, и из интересного спрашивали про having, lag/lead, внешние ключи, внешние таблицы, оптимизацию и план запросов, CDC (Change Data Capture), SSIS пакеты.
🍓 А сегодня прилетела вакансия из Wildberries вот с такими требованиями:
Задачи:
• Поддержка CI/CD-пайплайнов
• Настройка/поддержка кластера Clickhouse
• Организация ETL-процессов: маршрутизация потоков данных, обеспечение их качества и доступности
• Оптимизация сложных аналитических запросов в Clickhouse
• Бизнес-логика на Golang или Python
Стек:
• Python/Go (на уровне конкретных практических задач)
• СУБД Clickhouse, Postgres
• Источники данных: Kafka, NATS, OLAP-кубы, API
• k8s
Кому дать контакты?😉
#собес
Я думала, вот поставлю новую должность, тыкну «не ищу работу», и все посмотрят: «Ага, только устроилась, писать не будем». Но резюме всплыло наверх, и меня начали хантить уже через 3 дня после трудоустройства
- Билайн
- X5 Group
- Центр финансовых технологий
- Лига цифровой экономики
- ITFB Group
- Иннотех
- Mediascope
На некоторые собесы я ходила, и из интересного спрашивали про having, lag/lead, внешние ключи, внешние таблицы, оптимизацию и план запросов, CDC (Change Data Capture), SSIS пакеты.
Задачи:
• Поддержка CI/CD-пайплайнов
• Настройка/поддержка кластера Clickhouse
• Организация ETL-процессов: маршрутизация потоков данных, обеспечение их качества и доступности
• Оптимизация сложных аналитических запросов в Clickhouse
• Бизнес-логика на Golang или Python
Стек:
• Python/Go (на уровне конкретных практических задач)
• СУБД Clickhouse, Postgres
• Источники данных: Kafka, NATS, OLAP-кубы, API
• k8s
Кому дать контакты?
#собес
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13❤2💯1
Время задачек!🎉
Задача 1.
Эта задача на аналитика, она легкая, но мне понравилась.
Даны таблицы clients и documents.
В таблице с документами есть версионность, где '9999-12-31' означает, что версия действующая.
Нужно вывести id клиентов, у которых отсутствует актуальная версия.
Потестить можно тут.
Пишите свои решения в комментах!
Задача 1.
Эта задача на аналитика, она легкая, но мне понравилась.
Даны таблицы clients и documents.
В таблице с документами есть версионность, где '9999-12-31' означает, что версия действующая.
Нужно вывести id клиентов, у которых отсутствует актуальная версия.
Потестить можно тут.
Пишите свои решения в комментах!
create table clients (
client_id int,
client_fio varchar(255)
);
insert into clients values
(1, 'Иванов И.И.'),
(2, 'Петров П.П.'),
(3, 'Сидоров С.С.');
create table documents (
client_id int,
valid_from date,
valid_to date,
document_no varchar(9),
income int
);
insert into documents values
(1, '2020-01-01', '2020-12-31', '111111-11', 100000),
(1, '2021-01-01', '2022-06-01', '111111-11', 120000),
(1, '2022-06-01', '9999-12-31', '333333-33', 120000),
(2, '2001-01-01', '2023-01-01', '555555-55', 50000);
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥4
Задача 2.
Есть события, но часть из них доезжает позже других.
Нужно вывести такие события (5 и 6, которые вклинились).
Есть события, но часть из них доезжает позже других.
Нужно вывести такие события (5 и 6, которые вклинились).
create table logs (
id int,
dt date
);
insert into logs values
(1, '2023-12-01'),
(2, '2023-12-01'),
(5, '2023-12-02'),
(6, '2023-12-03'),
(3, '2023-12-06'),
(4, '2023-12-08');
🔥5
1) JOIN
Один подзапрос + один джойн
SELECT c.client_id
FROM clients c
LEFT JOIN (
SELECT DISTINCT client_id
FROM documents
WHERE valid_to = '9999-12-31'
) t1
ON c.client_id = t1.client_id
WHERE t1.client_id IS NULL
2) IN
Идём от обратного и исключаем
SELECT client_id
FROM clients
WHERE client_id NOT IN (
SELECT client_id
FROM documents
WHERE valid_to = '9999-12-31'
)
3) NOT EXISTS
Здесь можно оптимизировать, используя top/limit и константу.
Как это работает?
Если условие подзапроса соблюдается, мы берем просто цифру 1 вместо полей (не нужно лишний раз их вытаскивать) + одной строки нам достаточно, чтобы понять, что данные есть или их нет.
SELECT client_id
FROM clients c
WHERE NOT EXISTS (
SELECT 1
FROM documents d
WHERE c.client_id = d.client_id
AND valid_to = '9999-12-31'
LIMIT 1
)
Также важно внимательно читать задание и выводить то, что требуется (только id клиентов).
Решившим ребятам респект, остальные подключайтесь тоже!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤2🔥2
Один из вариантов был таким:
SELECT DISTINCT t1.*
FROM logs t1
JOIN logs t2
ON t1.id > t2.id AND t1.dt < t2.dt;
HashAggregate (cost=92291..92294)
Group Key: t1.id, t1.dt
-> Nested Loop (cost=0..89454)
Join Filter
-> Seq Scan on logs t1 (cost=0..33)
-> Materialize
-> Seq Scan on logs t2
Здесь очень дорогой Nested Loop Join, который увеличил косты с 33 до 90к.
Используем lag/lead и сравниваем разницу айдишников с предыдущим и последующим:
WITH diffs AS (
SELECT
*,
id - LAG(id) OVER(ORDER BY dt) prev_diff,
id - LEAD(id) OVER(ORDER BY dt) next_diff
FROM logs
)
SELECT id, dt
FROM diffs
WHERE prev_diff > 1 or next_diff > 1;
План запроса:
Subquery Scan on diffs (cost=159..249)
Filter
-> WindowAgg
-> Sort
Sort Key: logs.dt
-> Seq Scan on logs
В первом случае примерные косты были 90к, во втором 250 => в 370 раз меньше.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17
Помогите Санта Клаусу составить список, какие игрушки и в каких странах нужно будет докупить до нового года!
letters (id, date, child_id, toy_id)
children (id, name, country_id)
country (id, name)
toy (id, name, category)
И всех с наступающим!
Добра, вдохновения, мотивации, удачи, сил и прекрасного настроения!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9🎉2👍1 1
Итак, вариант от спасителя:
SELECT t.name, c.name, COUNT(*) AS count_toys
FROM country AS c
JOIN children AS ch
ON ch.country_id = c.id
JOIN letters as l
ON l.child_id = ch.id
JOIN toy as t
ON t.id = l.toy_id
WHERE l.date > '2023-01-01'
GROUP BY t.name, c.name
Обращаю внимание на один момент - фильтр на дату можно перенести повыше:
JOIN letters as l
ON l.child_id = ch.id
AND l.date > '2023-01-01'
При работе с базами данных может не быть разницы, какой из способов использовать.
Но при работе со спарком фильтр до джойнов уменьшит количество впустую обрабатываемых строк
Please open Telegram to view this post
VIEW IN TELEGRAM
Написала свою первую статью на хабре про то, как я анализирую свои доходы и расходы. Может, и вам будет полезно
https://habr.com/ru/articles/784412/
Please open Telegram to view this post
VIEW IN TELEGRAM
Хабр
Агрегатор личных финансов со всех счетов
Всем привет! Примерно год назад мне захотелось проанализировать доходы и расходы со всех своих банковских карт, количество которых начало разрастаться. После ресерча существующих приложений я поняла,...
🔥16🤡3❤1🌚1💯1🤝1
🌲Простой вопрос для новогодних каникул🌲
Запрос "SELECT * FROM test WHERE 0 = 1"
Запрос "SELECT * FROM test WHERE 0 = 1"
Anonymous Quiz
47%
корректный, вернется пустой результат без чтения таблицы
37%
корректный, вернется пустой результат, но условие будет проверяться на каждой строчке
16%
некорректный, будет ошибка
Поговорим про ✨ Apache Spark✨ - это движок/фреймворк для распределенной обработки больших данных.
Что значит распределенной?
Представь, что ты археолог и тебе нужно раскопать огромную территорию. Ты решил позвать n друзей, вы начали работать параллельно, и теперь вы закончите в n раз быстрее. Так и в спарке: каждая операция делится на маленькие таски, которые одновременно обрабатываются несколькими компьютерами, что ускоряет весь процесс.
Со спарком обычно работают на Python (через либу PySpark) и Scala.
Сначала нужно создать SparkSession:
Пару слов про code style в питоне. Есть два варианта:
1) обратный слэш
2) скобки
Я лично предпочитаю второй подход, потому что в первом нельзя закомментить строчки (только удалить, иначе синтаксическая ошибка) и нужно проставлять бэкслэши на каждой строке. Во втором - только один раз обрамить скобками, и все👌
#spark
Что значит распределенной?
Представь, что ты археолог и тебе нужно раскопать огромную территорию. Ты решил позвать n друзей, вы начали работать параллельно, и теперь вы закончите в n раз быстрее. Так и в спарке: каждая операция делится на маленькие таски, которые одновременно обрабатываются несколькими компьютерами, что ускоряет весь процесс.
Со спарком обычно работают на Python (через либу PySpark) и Scala.
Сначала нужно создать SparkSession:
from pyspark.sql import SparkSession
spark = (SparkSession.builder
.appName("SparkExample")
.master("yarn")
.config("spark.some.config.option", "config-value")
.enableHiveSupport()
.getOrCreate()
)
import org.apache.spark.sql.SparkSession
val spark = SparkSession.builder()
.appName("SparkExample")
.master("yarn")
.config("spark.some.config.option", "config-value")
.enableHiveSupport()
.getOrCreate()
Пару слов про code style в питоне. Есть два варианта:
1) обратный слэш
spark = SparkSession.builder \
.appName("SparkExample") \
...
2) скобки
spark = (SparkSession.builder
.appName("SparkExample")
...
)
Я лично предпочитаю второй подход, потому что в первом нельзя закомментить строчки (только удалить, иначе синтаксическая ошибка) и нужно проставлять бэкслэши на каждой строке. Во втором - только один раз обрамить скобками, и все
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17🆒4❤2💯2
Например, про временные таблицы
Это один из способов оптимизации запросов. Если разбить один длинный запрос на несколько временных таблиц, то он будет работать быстрее за счет минимизации повторных вычислений. Например, если нужно переиспользовать результат или если из-за джойнов сильно разрастается количество строк.
В MS SQL Server есть два вида: локальные (#) и глобальные (##):
CREATE TABLE #localTempTable (...)
CREATE TABLE ##globalTempTable (...)
Локальные доступны только для вашего пользователя и удаляются при закрытии сессии. Глобальные доступны всем и живут, пока жива хотя бы одна использующая их сессия.
На временные таблицы также можно навешивать индексы🗂
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7❤6⚡3
Отсортируйте по id, name по возрастанию и по убыванию суммы
SELECT id, name, CAST(amt AS float) AS amount FROM table1
SELECT id, name, CAST(amt AS float) AS amount FROM table1
Anonymous Poll
58%
ORDER BY id, name, amount DESC
40%
ORDER BY id, name, CAST(amt AS float) DESC
32%
ORDER BY 1, 2, 3 DESC
Все варианты валидны.
В сортировке можно использовать:
1) оригинальные поля
ORDER BY column_name
2) вычисляемые поля
ORDER BY fn(column_name)
3) элиасы (псевдонимы) полей
SELECT column_name AS column_name_alias
...
ORDER BY column_name_alias
4) числовые индексы полей (начиная с 1)
ORDER BY 1
Последний способ позволяет быстро написать запрос, но при невнимательности может возникнуть путаница в колонках. Лично я очень часто его использую, если нужно быстренько посмотреть))
А вам какой способ больше нравится?
#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Поправьте запрос:
SELECT date, trim(category) as new_category, COUNT(*) as cnt FROM table1
SELECT date, trim(category) as new_category, COUNT(*) as cnt FROM table1
Anonymous Poll
8%
1) GROUP BY date
0%
2) GROUP BY category
3%
3) GROUP BY new_category
4%
4) GROUP BY cnt
12%
5) GROUP BY date, category
33%
6) GROUP BY date, new_category
68%
7) GROUP BY date, trim(category)
40%
8) GROUP BY 1, 2
17%
9) GROUP BY date, 2
27%
10) GROUP BY 1, trim(category)
Верные варианты: 6, 7, 8, 9, 10.
Т.к. в селекте два поля таблицы, то они оба должны фигурировать в группировке (если у нас нет никаких ухищрений). Сама агрегирующая функция (count) никогда там не указывается.
В зависимости от диалекта некоторые варианты работать не будут (выделены
В группировке можно использовать:
1) оригинальные поля
GROUP BY column_name
2) вычисляемые поля
GROUP BY fn(column_name)
3) элиасы (псевдонимы) полей
SELECT column_name AS column_name_alias
...
GROUP BY column_name_alias
В Postgres, Clickhouse прокатит, а в MS SQL Server уже нет.
4) числовые индексы полей (начиная с 1)
GROUP BY 1
Аналогично
5) оригинальные поля в вычислениях
SELECT fn(column_name)
...
GROUP BY column_name
Последний вариант я нигде не встречала, и результат может быть не таким, какой вы ожидаете (в зависимости от функций). Но базы данных не запрещают группировать по существующим колонкам, даже если их нет в самом запросе.
Колонки и индексы можно комбинировать, но как по мне - лучше придерживаться одного стиля, иначе получается какая-то каша🥣
#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥2👍1
Spark-приложение управляется менеджером ресурсов YARN/Mesos/Standalone. У нас используется YARN. Он запускает приложение, выделяет ресурсы на вычисления и мониторит весь процесс.
Executor - исполнитель, выполняет Spark-код (например, "выведи 10 строчек из таблицы"). Их много.
Driver - драйвер, координирует работу экзекьюторов, планирует для них задачки и собирает результаты. А он такой один.
spark.driver.memory - объем памяти драйвера
spark.driver.cores - количество ядер драйвера
spark.driver.maxResultSize - максимальный размер результата, который передается от экзекьютеров к драйверу после вычислений
spark.executor.memory - объем памяти одного экзекьютора
spark.executor.cores - количество ядер экзекьютора
spark.executor.instances - количество экзекьюторов
Количество экзекьюторов и ядер влияет на скорость обработки данных за счет параллельного вычисления.
spark.local.dir - директория хранения временных файлов
spark.port.maxRetries - максимальное количество попыток подключения к порту (для UI, драйвера и т.д.)
Как применять:
spark = (
SparkSession.builder
.config("spark.driver.memory", "20g") # g - гигабайты, возможны 4: k, m, g, t
.config("spark.driver.cores", "2")
.config("spark.driver.maxResultSize", "20g")
.config("spark.executor.memory", "10g")
.config("spark.executor.cores", "2")
.config("spark.executor.instances", "20")
.config("spark.local.dir", "sparktmp")
.config("spark.port.maxRetries", "150")
...
)
Для запуска у себя на ноуте достаточно:
spark = (
SparkSession.builder
.appName("MySparkApp")
.master("local[*]") # * - все ядра, или указать число
.getOrCreate()
)
Более подробный список тут
Экзекьюторы нужно настраивать при статическом выделении ресурсов, а как это правильно делать и как настраивать динамически - расскажу в следующих постах
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤🔥3⚡2❤1
При статическом сколько экзекьюторов вы указали, столько и будет выделено для приложения. При динамическом это количество может меняться в зависимости от ваших настроек и свободных ресурсов.
spark.dynamicAllocation.enabled - флаг динамического/статического выделение ресурсов
spark.dynamicAllocation.initialExecutors - начальное количество экзекьюторов при старте
spark.dynamicAllocation.minExecutors - минимальное количество экзекьюторов
spark.dynamicAllocation.maxExecutors - максимальное количество экзекьюторов
spark.dynamicAllocation.executorIdleTimeout - время экзекьютора на ничегонеделание, по истечении которого он будет удален
spark.dynamicAllocation.cachedExecutorIdleTimeout - время экзекьютора с закешированными данными на ничегонеделание, по истечении которого он будет удален
Закешированные данные - когда результат вычисления хранится в памяти. Т.е. есть датафрейм, который что-то джойнит. Вы подождали 10 минут, пока посчитается, закешировали. В следующий раз обратились к тем же данным - и подождали уже 2 секунды.
Как применять:
spark = (
SparkSession.builder
.config("spark.dynamicAllocation.enabled", "true")
.config("spark.dynamicAllocation.initialExecutors", "2")
.config("spark.dynamicAllocation.minExecutors", "0")
.config("spark.dynamicAllocation.maxExecutors", "5")
.config("spark.dynamicAllocation.executorIdleTimeout", "600s") # 10 минут
.config("spark.dynamicAllocation.cachedExecutorIdleTimeout", "600s")
...
)
Когда кластер свободный, а вам надо обработать много данных, то это удобный способ. Но если вы долго ничего не считаете и таймауты из конфигов уже прошли, то у вас останется minExecutors. И если за это время кластер займут, то вы уже не сможете полноценно работать, пока не попросите коллег пожертвовать своими ресурсами
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4🔥4