tgindex

Дима SQL-ит 🧑‍💻 (Аналитика данных, AI)

описание

👨‍💻 Блог аналитика данных в IT 📩 По менторству и сотрудничеству: @catdem

2 103
подписчиков

Лучшие посты

за три месяца
  • 6 июл.1 725 просмотров62 реакций38 пересылок

    🏦 Разбор задачи с собеседования в Тинькофф (Т-банк). Задача про светофор сколько минут ты теряешь на красном? Условие: Каждый день едете на работу. На пути один светофор: 2 минуты горит зелёный, 1 минуту — красный. Сколько времени вы ожидаемо простоите за 100 поездок? Задачи на матожидание — любимый инструмент интервьюеров у аналитиков и DS. Выглядят как «логика про жизнь», а проверяют умение считать вероятности. Разберём на пальцах. 1️⃣ Находим цикл светофора Полный цикл = 2 + 1 = 3 минуты. Вы приезжаете к светофору в случайный момент — считаем, что равномерно распределённый по этим 3 минутам. 2️⃣ Считаем вероятности P(зелёный) = 2/3 → ждать 0 минут P(красный) = 1/3 → придётся постоять 3️⃣ Сколько ждать, если попали на красный? Вот тут сыплются многие. Вы ждёте не всю минуту. Вы приезжаете в случайный момент красной фазы: иногда в самом начале (придется ждать 1 минуту), иногда в конце (придется ждать 0 минут). В среднем — получим: Среднее ожидание на красном = (1+0)/2 = 0.5 минуты 4️⃣ Собираем матожидание за одну поездку E = P(зелёный) × 0 + P(красный) × 0.5 E = (2/3) × 0 + (1/3) × 0.5 E = 1/6 минуты ≈ 10 секунд 5️⃣ Умножаем на 100 поездок 100 × 1/6 ≈ 16.7 минуты = 16 минут 40 секунд Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Сколько у вас получилось до того, как дочитали до ответа? Делись в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 8 июн.1 558 просмотров47 реакций22 пересылок

    📊 SQL трюк — как добавить строку «Все категории» к любому отчёту через ROLLUP (ЧАСТЬ 2): В прошлом посте мы добавляли итоговую строку через UNION ALL — это рабочий метод, но есть способ элегантнее. Знакомьтесь: GROUP BY ROLLUP — встроенный механизм SQL для автоматических итогов. 🟢 Что такое ROLLUP на пальцах: ROLLUP — это расширение GROUP BY, которое автоматически добавляет строки с агрегатными итогами. Вместо того чтобы писать второй запрос и клеить через UNION ALL — просто добавляем одно слово 🟢 Шаг 1 — пишем запрос с ROLLUP: SELECT category, SUM(revenue) AS total_revenue, COUNT(order_id) AS orders_count FROM orders GROUP BY ROLLUP(category) Результат: category | total_revenue | orders_count Одежда | 7000 | 2 Обувь | 1200 | 1 Техника | 2500 | 1 NULL | 10700 | 4 Итоговая строка появилась автоматически, но в поле category стоит NULL — это сигнал ROLLUP, что строка является итогом по всей группе. 🟢 Шаг 2 — заменяем NULL на «Все категории»: Используем COALESCE, чтобы подменить NULL на читаемую метку: SELECT COALESCE(category, 'Все категории') AS category, SUM(revenue) AS total_revenue, COUNT(order_id) AS orders_count FROM orders GROUP BY ROLLUP(category) Результат: category | total_revenue | orders_count Одежда | 7000 | 2 Обувь | 1200 | 1 Техника | 2500 | 1 Все категории | 10700 | 4 Чисто и лаконично — никакого второго запроса. 🟢 Важный момент — а что если в данных уже есть NULL в category? Представим, что в таблице есть строки с незаполненной категорией: order_id | category | revenue 1 | Одежда | 1500 2 | Обувь | 1200 3 | NULL | 800 ← категория не указана 4 | Одежда | 5500 Запускаем запрос с COALESCE — и получаем проблему: category | total_revenue | orders_count Одежда | 7000 | 2 Обувь | 1200 | 1 Все категории | 800 | 1 ← это был NULL из данных! Все категории | 10700 | 4 ← это реальный итог ROLLUP COALESCE не различает: это NULL из данных или NULL от ROLLUP — он заменяет оба. 🟢 Решение — используем GROUPING(): Функция GROUPING(column) возвращает 1, если строка итоговая (от ROLLUP), и 0 — если это обычная строка с NULL из данных: SELECT CASE WHEN GROUPING(category) = 1 THEN 'Все категории' ELSE COALESCE(category, 'Не указана') END AS category, SUM(revenue) AS total_revenue, COUNT(order_id) AS orders_count FROM orders GROUP BY ROLLUP(category) ORDER BY GROUPING(category), category Результат: category | total_revenue | orders_count Не указана | 800 | 1 ← NULL из данных Одежда | 7000 | 2 Обувь | 1200 | 1 Все категории | 10700 | 4 ← итог ROLLUP Теперь всё на своих местах. 🟢 Хотите еще таких лайфхаков ? 🔥 Набираем 70 реакций на этот пост, чтобы подобные посты выходили чаще Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ А знали про ROLLUP и GROUPING() раньше? Делитесь в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 29 июн.1 520 просмотров24 реакций22 пересылок

    🗂 Порядок выполнения sql запроса — и почему алиас в WHERE не работает: Часто вижу одну и ту же ошибку — человек пишет запрос, придумывает алиас в SELECT, а потом пытается использовать его в WHERE или HAVING. И получает ошибку. Почему? Всё дело в порядке выполнения SQL. На самом деле SQL выполняется совсем не в том порядке, в котором мы его пишем. Вот реальная последовательность: 1️⃣ FROM — сначала движок идёт к таблицам 2️⃣ JOIN — потом движок соединяет таблицы 2️⃣ WHERE — фильтрует строки до группировки 3️⃣ GROUP BY — группирует данные 4️⃣ HAVING — фильтрует уже сгруппированные данные 5️⃣ SELECT — только сейчас вычисляются выражения и алиасы 6️⃣ ORDER BY — сортировка результата 7️⃣ LIMIT — обрезание до нужного числа строк Пример 1 — классическая ошибка с алиасом: -- ❌ Не работает — алиас revenue ещё не существует на шаге WHERE SELECT order_id, amount * 0.9 AS revenue FROM orders WHERE revenue > 1000; -- ✅ Правильно — повторяем выражение напрямую SELECT order_id, amount * 0.9 AS revenue FROM orders WHERE amount * 0.9 > 1000; Пример 2 — почему HAVING, а не WHERE: -- ❌ Нельзя — WHERE выполняется ДО GROUP BY SELECT user_id, COUNT(*) AS order_count FROM orders GROUP BY user_id WHERE order_count > 5; -- ✅ Правильно — HAVING фильтрует после группировки SELECT user_id, COUNT(*) AS order_count FROM orders GROUP BY user_id HAVING COUNT(*) > 5; Пример 3 — вот почему все пишут CTE: Иногда хочется сначала что-то посчитать, а потом фильтровать по результату. Именно для этого и придумали CTE — они позволяют «сохранить» промежуточный результат и работать с ним как с таблицей. -- ✅ CTE решает проблему: сначала считаем, потом фильтруем WITH revenue_calc AS ( SELECT order_id, amount * 0.9 AS revenue FROM orders ) SELECT * FROM revenue_calc WHERE revenue > 1000; Видите? В CTE SELECT уже выполнился — алиас revenue существует — и теперь во внешнем запросе WHERE отрабатывает по нему без проблем. Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Сталкивались с этой ошибкой? Или, может, объясняете это джунам — как объясняете? Пишите в комментах! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 23 мая1 489 просмотров32 реакций3 пересылок

    👍AHA 2026 - мой первый раз: Ранее писал про то, как я был первый раз на матемаркетинге, вот здесь можно почитать про это. Ну а сейчас напишу про мой первый раз на AHA 🤩 С кем удалось пообщаться и познакомиться: • 🛒 Ребята с Авито • 🥚 Ребята из МТС • 💬 Ребята из ВКонтакте • 🖼️ Ребята из Т1 • 🌟 Несколько IT-блогеров Завёл кучу полезных контактов — это, пожалуй, одна из главных ценностей таких мероприятий. Некоторые доклады послушаю в записи. Всё-таки доклад можно посмотреть потом, а живое общение — только в моменте. Поэтому ни о чём не жалею. На фото — всё, что удалось натаскать со стендов. Смотрите сами 😄 Итог: 🤩 ❓ Были на AHA 2026? Что смогли забрать со стендов? Пишите в комментариях — может, пересечёмся на следующем мероприятии в Москве! А пока хочу спросить: что хотите видеть дальше? • 🔧 Техничку — SQL, особенности СУБД, Python • 🏠 Материал про IT-ипотеку и ремонт (накопилось много — планирую начать писать про это) • 👇Или предложите свою тему в комментах 🌟 Бот с бесплатными материалами от автора канала: Бот_канала_dima_sqlit 🚬 Вопросы, менторство, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 5 июн.1 418 просмотров92 реакций5 пересылок

    🏠 IT ипотека (Часть 1) — выбор города и района: Решил задокументировать весь путь — от выбора места до финального результата. Это первый пост из серии, поехали. Почему вообще купил и зачем именно сейчас: Хотелось закрыть гештальт. Может, это и не самое выгодное финансовое решение, но психологически — точно правильное. Своя квартира = спокойствие (по крайней мере у меня так). Почему не Москва: Изначально хотел, конечно, Москву, но эту опцию быстро прикрыли и я не успел, но считаю, что IT ипотека на данный момент все равно один из самых привлекательных вариантов. Ну если не Москва, то ближайшее подмосковье подумал я и начал смотреть варианты, для меня основные параметры были это транспортная доступность и готовые варианты, чтобы быстро сделать ремонт и заехать. В итоге был выбран город Красногорск. Что по итогу выбрал: По итогу выбор пал на ЖК, который находится рядом с МЦД (в 10 минутах ходьбы). По отделке — это черновой вариант (стены и окна). White box оставались только трех-комнатные квартиры, что дорого и не ликвидно. Поскольку это черновой вариант решено было проектировать и разбираться во всем этом самостоятельно (благо есть знакомые и родственники с кем можно было советоваться). Подумал, что будет интересно под разобраться в этом (тем более по образованию я инженер-строитель) по этому в следующих постах буду рассказывать про ремонт, создание проекта, где я смог сэкономить и так далее. 🔥 Набираем 70 реакций на этот пост, чтобы посты про IT ипотеку выходили быстрее Итог: 🤩 ❓ А как вы относитесь к Ипотеке? ✔️ Подпишитесь на канал, чтобы не пропустить следующие посты. 🚬 Вопросы, менторство, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 22 июн.1 353 просмотров31 реакций15 пересылок

    🤖 Что такое MCP сервер — как научить нейросеть писать SQL под ваши данные (часть 2): В первой части разобрали что такое MCP и написали первый инструмент. Сегодня идем дальше — что делать, если запросы сложнее и нейросеть не знает как у вас организованы таблицы (их описание и как они могут соединяться между собой). Проблема: модель не знает ваши таблицы 🎚 Фиксированный тул — например get_sales_by_region — работает хорошо для простых запросов. Но что если пользователь спросит: «Сравни выручку по регионам с прошлым кварталом с разбивкой по сегментам клиентов»? Тут нужно несколько таблиц, JOIN и оконные функции. Заранее написать тул под каждый такой запрос — нереально (реально, конечно, но не логично мягко говоря). Решение: дать модели два инструмента — получить схему и возможность выполнить любой запрос. Как это устроено: 🎚 @mcp.tool() def get_database_schema(domain: str = "all") -> str: """ Возвращает схему БД: таблицы, поля, типы данных и связи. Вызывай ПЕРЕД тем как писать SQL-запрос. domain: 'sales', 'customers', 'products' или 'all' """ if domain == "all": files = ["sales.md", "customers.md", "products.md"] else: files = [f"{domain}.md"] result = "" for f in files: with open(f"docs/{f}", "r") as file: result += file.read() + "\n\n" return result @mcp.tool() def execute_sql(query: str) -> list: """ Выполняет SELECT-запрос к базе данных. Используй только после get_database_schema. Только SELECT — никаких изменений данных. """ conn = sqlite3.connect("metrics.db") cursor = conn.cursor() cursor.execute(query) result = cursor.fetchall() conn.close() return result Модель сначала вызывает get_database_schema, читает описание таблиц и связей — потом сама пишет SQL и передает в execute_sql. Как организовать файлы схемы: 🎚 Файлы лежат рядом с сервером в папке /docs. Внутри — описание таблиц, типы данных и как они связаны: ## Таблица orders - order_id INT — первичный ключ - customer_id INT — FK → customers.customer_id - region VARCHAR — регион продажи - revenue FLOAT — выручка - created_at DATE — дата заказа ## Связи orders.customer_id → customers.customer_id Прочитав это, модель сама поймет как делать JOIN между таблицами — объяснять ничего не нужно. Один MCP или несколько? 🎚 Один MCP-сервер = один проект. Не нужно плодить отдельный сервер под каждую таблицу. Все связанные таблицы и тулы живут в одном сервере. Если у вас две независимые системы — например аналитическая база и CRM — вот тогда имеет смысл делать два разных сервера. Здесь нет явного ответа, нужно подходить индивидуально к каждому проекту. А если схема хранится в Confluence? 🎚 Рабочий вариант — тул делает запрос к Confluence API и достает описание нужной таблицы прямо оттуда: @mcp.tool() def get_table_docs(table_name: str) -> str: """ Достает описание таблицы из корпоративной документации. Используй перед написанием SQL если нужны детали схемы. """ # запрос к Confluence API response = requests.get( f"{CONFLUENCE_URL}/rest/api/content", params={"title": table_name, "expand": "body.storage"}, auth=(USER, TOKEN) ) return response.json()["results"][0]["body"]["storage"]["value"] 🟢 Хотите еще про нейронки ? 🔥 Набираем 80 реакций на этот пост, чтобы подобные посты выходили чаще Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Какие идеи для использования MCP есть у вас? Делись в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 16 июн.1 350 просмотров51 реакций26 пересылок

    🤖 Что такое MCP — как дать нейросети доступ к вашим данным (часть 1): Если вы уже работаете в Cursor, Claude Code, Codex, LM Studio или другой IDE где есть нейросети — вы наверняка встречали термин MCP. Разберем, что это и зачем нужно. Что такое MCP? 🎚 MCP (Model Context Protocol) — открытый стандарт, который позволяет ИИ-клиенту подключаться к внешним данным и системам через специальные инструменты — Tools. Без MCP модель работает только с тем, что ты вставил ей в чат. Хочешь узнать метрику из своей базы — скопировал данные вручную, вставил, получил ответ. MCP убирает этот шаг: модель сама идет в твою базу, достает нужное и отвечает. Вы просто спрашиваете. Схема простая: • MCP Клиент — ваша среда (Cursor, Claude Desktop, Codex, LM Studio и другие) • MCP Сервер — ваш код с инструментами • Tool — функция, которую модель вызывает сама, когда понимает что она нужна Скиллы — это же то же самое? 🎚 Нет, в одном из прошлых постов я разбирал, что такое skills — по сути это просто большой сохраненный промпт для повторяющихся задач. Может показаться, что скилл умеет то же самое, что и mcp и возникает вопрос — зачем нужен mcp? Можно ведь написать скилл, который запускает bash или Python. Но вот в чём различия — это не скилл запускает скрипты. Cursor и другие IDE сами имеют встроенные инструменты: запуск команд, чтение файлов, редактирование кода — они зашиты внутрь приложения, никак не связаны с MCP. • Скилл — это промпт, который говорит модели «используй вот этот встроенный инструмент вот так». • MCP — другая история. Вы пишете свой код и подключаете его через протокол. Теперь у модели есть инструмент, который делает ровно то, что вы закодили — без ограничений клиента. Пишем свой MCP-сервер на Python: 🎚 Библиотека fastmcp делает это просто: from mcp.server.fastmcp import FastMCP import sqlite3 mcp = FastMCP("WorkMetrics") @mcp.tool() def get_sales_by_region(region: str) -> list: """ Возвращает продажи по указанному региону за текущий месяц. Используй когда пользователь спрашивает о продажах или выручке по региону. """ conn = sqlite3.connect("metrics.db") cursor = conn.cursor() cursor.execute( "SELECT date, revenue FROM sales WHERE region=?", (region,) ) result = cursor.fetchall() conn.close() return result if __name__ == "__main__": mcp.run() Прописываем сервер в mcp.json — и всё. Пишете в чате: «Покажи продажи по Москве» — модель сама вызывает нужный инструмент. 🟢 Хотите еще про нейронки ? 🔥 Набираем 80 реакций на этот пост, чтобы подобные посты выходили чаще Итог: 🤩 На посты про MCP меня вдохновило вот это видео — рекомендую к просмотру для большего понимания. ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Уже пробовал MCP если да, то какие? Делись в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 3 июн.1 348 просмотров62 реакций13 пересылок

    📊 SQL трюк — как добавить строку «Все категории» к любому отчёту через UNION ALL (ЧАСТЬ 1): Ситуация, с которой многие сталкиваются на работе. Давайте представим таблицу с продажами orders: order_id | category | revenue 1 | Одежда | 1500 2 | Обувь | 1200 3 | Техника | 2500 4 | Одежда | 5500 ......... И нужно сделать отчёт по продажам, который будет показывать в разрезе категорий тотал продажи — и при этом должна быть отдельная категория, которая объединяла бы все остальные. Её как раз мы искусственно и добавим через UNION ALL. 🟢 Шаг 1 — обычная разбивка по категориям: SELECT category, SUM(revenue) AS total_revenue, COUNT(order_id) AS orders_count FROM orders GROUP BY category Получаем это: category | total_revenue | orders_count Одежда | 7000 | 2 Обувь | 1200 | 1 Техника | 2500 | 1 Хорошо, но итога по всем нет. Добавляем его через UNION ALL. 🟢 Шаг 2 — добавляем строку «Все категории»: SELECT category, SUM(revenue) AS total_revenue, COUNT(order_id) AS orders_count FROM orders GROUP BY category UNION ALL SELECT 'Все категории' AS category, SUM(revenue), COUNT(order_id) FROM orders Результат: category | total_revenue | orders_count Одежда | 7000 | 2 Обувь | 1200 | 1 Техника | 2500 | 1 Все категории | 10700 | 4 Второй запрос считает тотал по всей таблице, а в поле category мы просто подставляем строку 'Все категории' — и она встаёт отдельной строкой в результате. Почему UNION ALL, а не просто UNION? UNION под капотом делает сортировку и удаляет дубликаты — это лишняя работа для базы. UNION ALL просто склеивает результаты как есть, без лишних операций. В нашем случае дублей нет — строка 'Все категории' явно уникальна. Так что UNION тут ни к чему, берём UNION ALL — он быстрее. 🟢 Бонус — строка с тоталом всегда внизу: WITH combined AS ( SELECT category, SUM(revenue) AS total_revenue, COUNT(order_id) AS orders_count FROM orders GROUP BY category UNION ALL SELECT 'Все категории', SUM(revenue), COUNT(order_id) FROM orders ) SELECT * FROM combined ORDER BY CASE WHEN category = 'Все категории' THEN 1 ELSE 0 END, category 🟢 Хотите еще таких лайфхаков ? 🔥 Набираем 70 реакций на этот пост, чтобы подобные посты выходили чаще Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ А какие ещё задачи решаете через UNION ALL — поделитесь в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 28 мая1 293 просмотров94 реакций24 пересылок

    ❤️ Переменные в SQL: один раз написал — везде обновилось Вам знакома ситуация, когда - написал большой sql запрос для задачи, а потом заказчик говорит: «Покажи то же самое, но за прошлый квартал» ? И вы начинаете искать дату по всему файлу. Находите все вхождения, правите — и где-то всё равно пропускаешь одно. Запрос считает не то. В общем очень много рутины. 🟢 В Python мы бы написали переменную в начале и пере использовали ее везде: start_date = "2024-01-01" discount_threshold = 0.10 🟢 Но в SQL тоже можно кое что придумать: WITH params AS ( SELECT DATE '2024-01-01' AS period_start, DATE '2024-03-31' AS period_end, 0.10 AS discount_threshold ), big_orders AS ( SELECT o.order_id, o.amount, o.discount FROM orders o CROSS JOIN params p WHERE o.order_date BETWEEN p.period_start AND p.period_end AND o.discount > p.discount_threshold ) SELECT * FROM big_orders Все параметры — в одном месте сверху. Поменял квартал один раз — везде пересчиталось. CROSS JOIN здесь — это не страшно. params возвращает одну строку. CROSS JOIN просто «прицепляет» эту строку к каждой строке основной таблицы. 🟢 Хотите еще таких лайфхаков ? 🔥 Набираем 70 реакций на этот пост, чтобы подобные посты выходили чаще Итог: 🤩 🗂 Это один из тех приёмов, который занимает 10 секунд, но экономит кучу времени на поддержке запроса. ❓ А как вы решаете эту проблему — хардкодите или есть другой подход? Пишите в комментах! 📤 Пересылай пост коллеге или другу из аналитики — это бесплатно и помогает каналу расти. Спасибо! 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 1 июн.1 233 просмотров36 реакций14 пересылок

    🔐 SQL-инъекция — типы, примеры и защита: всё что нужно знать аналитику: Один из вопросов, который может быть на собесе - это что такое "SQL-инъекция". Давайте разберёмся, как обычно с кодом и без воды. 🟢 Как работает атака: Бэкенд строит запрос прямо из того, что ввёл пользователь: SELECT * FROM users WHERE username = 'ВВОД' AND password = 'ВВОД'; Кавычки вокруг ввода уже вшиты в шаблон кода. Злоумышленник вводит admin' -- и запрос превращается в: WHERE username = 'admin' --' AND password = '...'; Левая кавычка уже есть в шаблоне — злоумышленник дописывает только закрывающую '. Она обрывает строку досрочно, -- превращает всё остальное в комментарий. Проверка пароля выброшена — вход открыт. 🟢 3 основных типа SQL-инъекций: 1️⃣ Классическая — результат виден прямо в ответе сайта. Самый простой случай как выше. 2️⃣ Слепая (Blind) — сайт не выводит данные, но страница реагирует по-разному. Злоумышленник добавляет условие через AND к рабочему запросу: SELECT * FROM products WHERE id = 1 AND SUBSTRING(password, 1, 1) = 'a' Если буква угадана → TRUE AND TRUE → товар найден → страница загрузилась Если не угадана → TRUE AND FALSE → товар не найден → страница пустая По реакции страницы злоумышленник понимает верна ли буква — как игра «горячо-холодно». Перебирает символ за символом. 3️⃣ Через UNION — дописывает свой SELECT к оригинальному и вытаскивает данные из любой таблицы. Структуру БД знать заранее не нужно — она вытаскивается через information_schema, системный справочник который есть в каждой БД: -- Шаг 1: узнать все таблицы ' UNION SELECT table_name, null FROM information_schema.tables -- -- Шаг 2: узнать поля нужной таблицы ' UNION SELECT column_name, null FROM information_schema.columns WHERE table_name = 'users' -- -- Шаг 3: вытащить данные ' UNION SELECT username, password FROM users -- 🟢 Как защититься: Без защиты Python склеивает строку и отдаёт в БД целиком — данные и команды уже неразличимы: # ПЛОХО query = "SELECT * FROM users WHERE username = '" + username + "'" С параметрами шаблон и данные уходят в БД раздельно: # ХОРОШО cursor.execute( "SELECT * FROM users WHERE username = %s", (username,) ) БД сначала получает шаблон → разбирает и фиксирует структуру запроса. Потом получает данные — и воспринимает их только как текст для поиска. Апостроф внутри admin' -- уже просто символ, а не SQL-синтаксис — структура зафиксирована и данные её изменить не могут. 🟢 Ответ на собесе — коротко: «SQL-инъекция — это вид атаки, когда пользовательский ввод попадает напрямую в строку SQL-запроса и исполняется как код. Защита — параметризованные запросы: шаблон и данные идут в БД раздельно, поэтому данные никогда не интерпретируются как SQL-команды.» Итог: 🤩 ❓ Спрашивали про SQL-инъекцию на вашем собесе? Пишите в комментарии! ✔️ Подпишитесь на канал, чтобы не пропустить следующие посты. 🚬 Вопросы, менторство, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 21 июл.1 191 просмотров27 реакций23 пересылок

    Собесы, собесы, собесы… Если тебе интересны обзоры собеседований и разборы заданий, то можешь заглянуть на канал Айти-Пингвин | Дата инженер. Это канал дата-инженера, который активно делает обзоры пройденных собесов, разбирает задачки и технологии, а также рассказывает о своем карьерном пути. Что, на мой взгляд, будет полезно для собесов на аналитика? 1. На джуна разбор вопросов по SQL , вопросы про джоины и задача на джоины. 2. На мидла - задачи по SQL + оптимизация SQL-запросов. Будет очень полезно и джунам. 3. Порадовали задачи на адекватность. 😅 Будь готов, что на собесе спросят что-то совсем базовое и простое, но некоторых такие задачи вгоняют в ступор :) С разрешения Пингвина приведу загадки с собеседования. Вот еще несколько интересных постов: • Статья по индексам и партициям • Транзакции и ACID • Итоги за год • Удаление дублей в Greenplum • История о потере данных Из последнего: • обзоры собесов в сбер (1, 2, GigaData) • Neoflex • Обзор большого собеседования с IT_One • Лига цифровой экономики (проект Альфа банк) • Популярные вопросы с hr скрининга • Очередные вопросы по SQL с собеседования В целом, на канале разнообразный контент. Заходите и изучайте😁 it пингвин | data engineer 🐧

  • 19 июн.1 165 просмотров56 реакций18 пересылок

    🏠 IT ипотека (Часть 2) — как я проектировал квартиру (Remplanner, Pinterest, ChatGPT): Когда берёшь черновую квартиру — впереди не просто ремонт, а целый проект. Надо придумать планировку, расстановку мебели и понять, как это вообще будет выглядеть в натуре. Делюсь своим стеком инструментов которые я использовал. 🎚 Инструмент 1 — Remplanner: Это был мой основной инструмент на этапе планировки квартиры. Работает прямо в браузере — по сути autocad, но с большим количеством готовых инструментов и шаблонов. Что я делал в Remplanner: • Прорабатывал разные варианты планировки • Расставлял мебель и проверял, насколько удобно всё помещается • Планировал розетки, выключатели и освещение • Смотрел, как будут выглядеть комнаты в 3D • Формировал понятные чертежи для строителей Самое полезное — можно быстро делать несколько вариантов и сравнивать их между собой. На бумаге многие идеи выглядят отлично, а в планировщике сразу становится видно, где не открывается шкаф, не проходит стол или остаётся слишком узкий проход. В итоге ещё до начала ремонта понимаешь, как будет выглядеть квартира и избегаешь части дорогих переделок. 🎚 Инструмент 2 — Pinterest: Это этап «насмотренности». Работает так: • Вбиваете запрос: например, «квартира 45 кв», «скандинавский стиль серый» — и просто смотришь • Постепенно понимаешь, что тебе нравится, а что нет • Сохраняешь понравившееся → вырисовывается общее направление Повторить всё один в один с Pinterest не получится — дорого или нереализуемо. Но это и не нужно. Важно сформировать своё видение. 🎚 Инструмент 3 — ChatGPT: Когда стены уже возведены — делаешь фотографию реального пространства и загружаешь в ChatGPT. Дальше: • «Хочу здесь поставить диван, что посоветуешь?» • «Как можно оформить этот угол?» • «Что сюда органично впишется по цвету?» ChatGPT накидывает варианты прямо под ваш конкретный интерьер (приложил пример, как я подбирал кухню). 🔥 Набираем 70 реакций на этот пост, чтобы посты про IT ипотеку выходили быстрее Итог: 🤩 ❓ Чем пользовались при ремонте или перестановке? Делитесь в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие посты. 🚬 Вопросы, менторство, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 22 июл.1 164 просмотров32 реакций21 пересылок

    🛒 Разбор задачи с собеседования Магнит OMNI (MAGNIT TECH). Сколько покупок нужно, чтобы собрать все 10 стикеров? Условие: В наборе 10 наклеек. На кассе дают одну случайную за любую покупку. сколько покупок нужно, чтобы собрать все 10? Задача на пониманием и умение считать математическое ожидание. Разберём на пальцах: 1️⃣ Главное правило: переворачиваем дробь Если вероятность равна p, то среднее количество попыток до первого успеха равно 1/p. Например: Вероятность 1/10 → 10/1 = 10 попыток Вероятность 5/10 → 10/5 = 2 попыток 2️⃣ Шанс поймать новую наклейку Собираем по этапам. Чем больше наклеек в альбоме, тем реже попадается новая: этап 1: собрано 0 → шанс новой 10/10 этап 2: собрано 1 → шанс новой 9/10 ... этап 10: собрано 9 → шанс новой 1/10 3️⃣ Собираем матожидание и считаем ответ Переворачиваем шанс каждого этапа в число покупок и складываем всё: этап 1 (запишем общую формулу): E = 10/10 + 10/9 + 10/8 + ... + 10/2 + 10/1 этап 2 (вынесем общий множитель за скобки): E = 10 × (1 + 1/2 + 1/3 + ... + 1/10 этап 3 (считаем ответ): E = 10 × 2.93 ≈ 29 покупок Почти половина из этих них уходит на последние 2-3 наклейки. Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Сколько у вас получилось до того, как дочитали до ответа? Делись в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 31 июл.949 просмотров19 реакций20 пересылок

    🎲 Формула Байеса и условная вероятность — разбор на кубике: Условие: кинули кубик и говорят — выпало чётное. Какова вероятность, что это шестёрка? Ответ: не 1/6, а 1/3. Покажу через формулу Байеса и расшифрую в ней каждое слагаемое — это и есть самое главное. 1️⃣ Обозначим события: A — «выпала шестёрка» (это мы ищем) B — «выпало чётное» (это уже известно — условие) Нужна вероятность A, если известно B. 2️⃣ Сама формула Байеса: P(A|B) = P(B|A) × P(A) / P(B) Читается: вероятность A при условии, что произошло B. 3️⃣ Разбираем каждый кусок формулы: • P(A|B) — что ищем: шанс шестёрки, раз выпало чётное. Это и есть ответ. • P(A) — базовый шанс шестёрки, без подсказок = 1/6. • P(B) — шанс чётного вообще: чётные это 2, 4, 6, то есть 3 из 6 = 1/2. • P(B|A) — шанс, что чётное, ЕСЛИ выпала шестёрка = 1. Шестёрка всегда чётная. 4️⃣ Подставляем числа: P(A|B) = P(B|A) × P(A) / P(B) = 1 × (1/6) / (1/2) = (1/6) / (1/2) = 1/3 5️⃣ Почему получилось именно так: Подсказка «чётное» выкинула варианты 1, 3, 5. Осталось 2, 4, 6 — и шестёрка одна из трёх. Формула дала ровно это: 1/3. Информация подняла шанс шестёрки с 1/6 до 1/3 — в этом вся суть Байеса. Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ А вы сразу сказали 1/3 или попались на 1/6? Делитесь в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие разборы. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 28 июл.921 просмотров43 реакций13 пересылок

    📊 DAU, WAU, MAU и sticky factor — метрики активности: Пришло время разбираться с метриками, начнем с DAU, WAU, MAU и sticky factor 😘 1️⃣ Что это DAU — уникальные пользователи за день WAU — за 7 дней MAU — за 30 дней Ключевое слово — уникальные: один user_id считаем один раз, сколько бы раз он ни заходил. 2️⃣ Считаем все три одним запросом SELECT COUNT(DISTINCT CASE WHEN dt = CURRENT_DATE THEN user_id END) AS dau, COUNT(DISTINCT CASE WHEN dt >= CURRENT_DATE - 6 THEN user_id END) AS wau, COUNT(DISTINCT CASE WHEN dt >= CURRENT_DATE - 29 THEN user_id END) AS mau FROM events; COUNT(DISTINCT …) убирает повторы, CASE WHEN задаёт окно по дате. 3️⃣ Частая ошибка WAU — это не сумма семи DAU. Кто заходил все 7 дней, в сумме DAU посчитан 7 раз, а в WAU — один. Отсюда правило: DAU ≤ WAU ≤ MAU. 4️⃣ Sticky factor sticky factor = DAU / MAU — показывает, как часто пользователи возвращаются. 0.1 → заходят ~3 дня в месяц (слабо) 0.2 → ~6 дней 0.5 → ~15 дней (уровень соцсетей) Чем выше — тем «липче» продукт. Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Как вам такие разборы в формате видео? Делись в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 4 авг.918 просмотров25 реакций21 пересылок

    🏦 Разбор задачи с собеседования в Т-Банке (Тинькофф). Тест точен на 99%, он положительный — но вы, скорее всего, здоровы: Ранее мы разобрали, как работает условная вероятность и формула Байеса. Если пропустили, то вот сам пост. Давайте разберем на эту тему популярную задачу, которую спрашиваю во многих компаниях. Условие: болезнь встречается у 1% людей. Тест находит болезнь у больного в 99% случаев, а у здорового ошибочно кричит «+» в 5% случаев. Вам пришёл положительный результат. Какова вероятность, что вы действительно больны? Почти все на автомате отвечают «99%». Правильный ответ — 16.7%. Разберём, почему. 1️⃣ Вопрос не тот, на который вы ответили: В условии дано P(+ | болен) = 99% — «тест сработает, если человек болен». А спрашивают P(болен | +) — «человек болен, если тест сработал». Это разные вопросы, и цифры у них разные. Формула Байеса как раз переворачивает условную вероятность с одной стороны на другую. 2️⃣ Считаем по головам — 10 000 человек: Забудем про проценты и посчитаем живых людей: • больных: 1% от 10 000 = 100 человек • здоровых: 9 900 человек Прогоняем всех через тест: • из 100 больных тест поймает 99% = 99 человек • из 9 900 здоровых тест ошибочно испугает 5% = 495 человек тест + тест − всего болен 99 1 100 здоров 495 9405 9900 всего 594 9406 10000 Смотрим столбец «тест +»: плюс получили 594 человека, а больны из них только 99. P(болен | +) = 99 / 594 = 1/6 ≈ 16.7% 3️⃣ Тот же ответ формулой Байеса: Сначала выпишем всё, что даёт условие — новых чисел дальше не появится: P(болен) = 0.01 — болезнь у 1% людей P(здоров) = 0.99 — остальные 99% P(+ | болен) = 0.99 — тест ловит больного P(+ | здоров) = 0.05 — ошибка на здоровом ⚠️ Два разных числа выглядят одинаково: 0.99 — это и точность теста, и доля здоровых. Не перепутайте, кто где стоит. Числитель — только больные с плюсом: P(болен) × P(+|болен) = 0.01 × 0.99 = 0.0099 → это наши 99 человек Знаменатель — почему он именно такой Мы ищем долю: из всех, у кого плюс, сколько по-настоящему больны. Значит внизу должны стоять ВСЕ плюсы, а не только больные — иначе это будет не доля. А плюс может прийти ровно из двух мест: • тест поймал больного → 0.01 × 0.99 = 0.0099 → 99 человек • тест ошибся на здоровом → 0.99 × 0.05 = 0.0495 → 495 человек Третьего источника нет: человек либо болен, либо здоров, других вариантов не бывает. Поэтому знаменатель — сумма этих двух групп (это и есть формула полной вероятности): P(+) = 0.0099 + 0.0495 = 0.0594 → 594 человека Внутри каждой группы мы умножаем по одному правилу: «какая группа по размеру» × «какая доля в ней даёт плюс». Для здоровых это 0.99 × 0.05 — 99% людей здоровы, и 5% из них тест пугает зря. Делим: 0.0099 / 0.0594 ≈ 0.167 = 16.7% Ровно те же 99 и 594, что мы посчитали по головам. Формула ничего не выдумывает — она просто записывает подсчёт людей на языке вероятностей. Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Встречали эту задачку на собеседованиях? Пишите в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 3 авг.785 просмотров27 реакций11 пересылок

    💰 ARPU vs ARPPU — разберемся в чем разница: ARPU и ARPPU постоянно путают. Обе про доход с пользователя, но делят на разное. Разберём и выведем главную формулу. 1️⃣ ARPU — доход на ВСЕХ Всю выручку делим на всех активных, даже на тех, кто ничего не заплатил. ARPU = выручка / все юзеры SELECT SUM(revenue) / COUNT(DISTINCT user_id) AS arpu FROM payments; 2️⃣ ARPPU — доход на ПЛАТЯЩИХ Та же выручка, но делим только на тех, у кого платёж больше нуля. ARPPU = выручка / платящие SELECT SUM(revenue) / COUNT(DISTINCT CASE WHEN revenue > 0 THEN user_id END) AS arppu FROM payments; 3️⃣ Связь — доля платящих доля платящих = платящие / все юзеры = CR to pay (часто 1–5%) Отсюда главное тождество: ARPU = ARPPU × доля платящих И всегда ARPPU >= ARPU. 4️⃣ Куда расти Тождество показывает два рычага роста: • поднять долю платящих — онбординг, триггеры оплаты • поднять ARPPU — апселл, тарифы, допродажи Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Что еще разобрать? Пишите в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 11 авг.581 просмотров29 реакций20 пересылок

    🛒 Разбор задачи с собеседования в Авито: Условие: в мешке три кубика — на 6, 12 и 20 граней. Взяли один наугад, бросили — выпало 12. Бросаем этот же кубик ещё раз. Какова вероятность, что выпадет число меньше 12? 1️⃣ Что понятно сразу 6-гранный отпадает: числа 12 на нём просто нет. Значит в руке либо 12-гранный, либо 20-гранный. 2️⃣ Почему не 50 на 50 12 выпадает на этих кубиках по-разному: на 12-гранном — 1 раз из 12, на 20-гранном — 1 раз из 20. Раз у вас она выпала, вероятнее, что в руке именно 12-гранный. 3️⃣ Насколько именно чаще Чтобы сравнить кубики честно, дадим каждому одинаковое число бросков. Возьмём 60 — оно делится и на 12, и на 20, поэтому получатся целые числа: 12-гранный: двенадцатка выпадет 60 : 12 = 5 раз 20-гранный: двенадцатка выпадет 60 : 20 = 3 раза Итого 12-ть выпало 8 раз: пять раз с 12-гранного и три — с 20-гранного. А если взять не 60 бросков, а другое число? Ничего не изменится — важна только пропорция, а не сама цифра: по 120 бросков → 10 и 6 по 600 бросков → 50 и 30 по 60 бросков → 5 и 3 Везде одно и то же: 12-гранный даёт 12-ать в 5 случаях из 8. Число 60 удобно только тем, что делится нацело: возьми 50 — вышло бы 4.17 и 2.5, та же пропорция. При чём тут эти 8 случаев По условию 12-ать уже выпало. Значит все броски, где выпало что-то другое, к вам не относятся — вы находитесь внутри тех самых 8 случаев. И в пяти из них у вас в руках 12-гранный кубик. Вот и всё, никакого 50 на 50. 4️⃣ Второй бросок Считаем, как часто на каждом кубике выпадает меньше 12. Подходят числа от 1 до 11 — это 11 нужных граней: на 12-гранном: 11 граней из 12 = 92% на 20-гранном: 11 граней из 20 = 55% Осталось соединить это с восемью случаями. В пяти из них у вас в руках 12-гранный и шанс 92%, в трёх — 20-гранный и шанс 55%. Считаем среднее по всем восьми, как среднюю оценку в классе: (5 × 92% + 3 × 55%) / 8 ≈ 78% 5️⃣ Второй случай из тестового: выпало 4 Логика ровно та же, только теперь подходят все три кубика. Снова по 60 бросков каждым: 6-гранный: 60 : 6 = 10 четвёрок 12-гранный: 60 : 12 = 5 20-гранный: 60 : 20 = 3 всего 18 случаев Меньше 4 — это 1, 2 и 3, то есть 3 нужные грани: на 6-гранном: 3 из 6 = 50% на 12-гранном: 3 из 12 = 25% на 20-гранном: 3 из 20 = 15% Среднее по восемнадцати случаям: (10 × 50% + 5 × 25% + 3 × 15%) / 18 ≈ 37% Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Встречали эту задачку на собеседованиях? Пишите в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit

  • 13 авг.468 просмотров28 реакций10 пересылок

    🌐 Разбор задачи с собеседования в Яндекс. Парадокс Монти Холла: Условие: Три двери, за одной приз, за двумя пусто. Вы выбрали дверь 1. Ведущий открыл дверь 3 — там пусто — и предлагает поменять выбор на дверь 2. Стоит ли изменить выбор? 1️⃣ Ловушка Дверей осталось две, приз за одной. Кажется, шансы равны — 50 на 50. Это самый частый ответ и он неверный. Ошибка в том, что мы считаем оставшиеся двери одинаковыми. А они не одинаковые: одну выбрали вы наугад, вторую оставил ведущий, который знает расклад. 2️⃣ Деталь, на которой держится вся задача Ведущий знает, где приз. И у него связаны руки: — вашу дверь он не трогает — дверь с призом он не открывает Значит его ход не случаен. Он не «просто открыл пустую дверь» — он был обязан открыть именно пустую. И этим он сливает вам информацию 3️⃣ Перебор: три расклада, больше их не бывает Ваш выбор — дверь 1. Приз может быть за любой из трёх дверей, и все три случая равновероятны. Просто пройдём их по очереди и посмотрим, что даёт СМЕНА выбора. • Приз за дверью 1 (вы угадали сразу) Ведущий открывает 2 или 3 — обе пустые. Меняете → попадаете на пустую. ✗ мимо • Приз за дверью 2 Ваша дверь 1 пустая, дверь 3 пустая. Открыть 1 он не может (ваша), открыть 2 не может (там приз) — остаётся дверь 3. Меняете → берёте дверь 2 с призом. ✓ приз • Приз за дверью 3 Зеркально: он обязан открыть 2. Меняете → берёте дверь 3 с призом. ✓ приз Итого смена выигрывает в 2 случаях из 3. 4️⃣ Почему так получается Посмотрите, когда смена проигрывает. Ровно в одном случае — если вы угадали приз с первого раза. А это 1 шанс из 3. Дальше просто: вы угадали сразу → 1/3 → смена проигрывает вы не угадали → 2/3 → смена ВСЕГДА выигрывает Во втором случае смена выигрывает не «иногда», а всегда: если ваша дверь пустая, то из двух оставшихся одна с призом, и пустую ведущий уже открыл за вас. Он своими руками сложил все ваши 2/3 в одну-единственную дверь. Остаться: 1/3 (33%) Поменять: 2/3 (67%) Ваш первый выбор как был 1/3, так им и остался — ведущий про вашу дверь ничего не сообщил, он и не мог её открыть. Зато про две другие сообщил всё. Итог: 🤩 ❤️ Поддержать канал бустами, чтобы у автора появился дополнительный функционал можно - здесь (это бесплатно и доступно с подпиской telegram premium) ❓ Какой ответ вы дали первым — 50 на 50 или сразу «менять»? И смогли бы объяснить почему? Пишите в комментариях! ✔️ Подпишитесь на канал, чтобы не пропустить следующие хаки. 🚬 Вопросы, обучение, консультации: Написать в ЛС | mentor.dima-sqlit.ru @dima_sqlit