Отсортируйте по 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
SELECT *
FROM test1
JOIN test2
ON cast(test1.id as varchar) = test2.id
или
ON test1.id = cast(test2.id as int)
Некоторые бд сами подсвечивают ошибку при запуске запроса, а некоторые начинают его выполнение, но затрачивают сильно больше ресурсов на неявное преобразование. И запрос, который мог отработать за 5 минут, может отработать за 30 или не отработать вообще. Поэтому за соответствием типов важно следить
#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
💯6⚡5❤2❤🔥2👏1
# сессия
from pyspark.sql import SparkSession
# функции
from pyspark.sql import functions as F
# типы данных
from pyspark.sql import types as T
# оконки
from pyspark.sql.window import Window
F и T - это code-style, принятый в PySpark, чтобы избежать пересечений с другими либами. В коде будет так: F.function(args).
И вообще импортируем только то, что нужно. import * - это моветон.
// датафрейм и сессия
import org.apache.spark.sql.{DataFrame, SparkSession}
// функции
import org.apache.spark.sql.functions._ // импорт всего
// udf (кастомные функции) и оконки
import org.apache.spark.sql.expressions.{UserDefinedFunction, Window}
// типы данных
import org.apache.spark.sql.types._
В отличие от питона, в скале нужно указывать типы аргументов в функциях, поэтому мы дополнительно импортируем DataFrame, UserDefinedFunction и Window, т.к. они наиболее часто используются. А сами оконки лежат в модуле functions.
def func(df: DataFrame, time_window: Window): DataFrame = {...}#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10
Бывает так, что есть поле, по которому нужно отсортировать, но по алфавиту ну никак не подходит. Тогда можно искусственно создать поле для сортировки через case when и его аналоги:
SELECT color
FROM test
ORDER BY
CASE color
WHEN 'red' THEN 1
WHEN 'orange' THEN 2
WHEN 'yellow' THEN 3
WHEN 'green' THEN 4
WHEN 'blue' THEN 5
WHEN 'indigo' THEN 6
WHEN 'violet' THEN 7
END
#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥38
Объявление переменных Python🐍 vs Scala🏔
Если мы хотим объявить несколько переменных в одной строке, на Python это будет так:
На Scala нужно добавить ключевое слово val/var и обрамить скобочками:
Для удобства можно указать типы полей:
val - неизменяемый, от слова "value"
var - изменяемый, от слова "variable"
По возможности старайтесь использовать константы, это:
✅ понижает риски появления багов
✅ повышает читаемость кода
✅ позволяет оптимизироваться под капотом, зная, что значения меняться не будут
В Scala есть еще одна интересность - lazy val.
val выполняется, когда мы создали переменную.
lazy val выполняется, когда мы впервые обратились к этой переменной.
Если мы никогда до нее не доберемся, то ресурсы тратится не будут💳
#python_tips #scala_tips
Если мы хотим объявить несколько переменных в одной строке, на Python это будет так:
a, b = 'a', 1.1
На Scala нужно добавить ключевое слово val/var и обрамить скобочками:
val (a, b) = ("a", 1.1)Для удобства можно указать типы полей:
val (a:String, b:Double) = ("a", 1.1)val - неизменяемый, от слова "value"
var - изменяемый, от слова "variable"
По возможности старайтесь использовать константы, это:
В Scala есть еще одна интересность - lazy val.
val выполняется, когда мы создали переменную.
lazy val выполняется, когда мы впервые обратились к этой переменной.
Если мы никогда до нее не доберемся, то ресурсы тратится не будут
#python_tips #scala_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤5💯1
Почему вчера запрос отрабатывал 5 минут, а сегодня 2 часа?
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Так почему запрос стал медленнее?
Это вопрос с собесов, и тут главное - не перечислить все варианты, а показать, что вы умняши и соображаете🤓
📝 Я бы выделила вот эти группы:
- появилось в разы больше данных
- параллельно с таблицей что-то происходит, и она залочена
- меньше ресурсов
- поменялись индексы
- поменялся план запроса (например, нужно помочь и добавить хинты)
- сетевые проблемы
- конфигурационные изменения (админы взяли и уменьшили параметры)
💬 Очень полезные варианты - в комментариях к предыдущему посту.
🧐 И, прежде чем столько ждать, лучше провести свой анализ, посмотреть на каунты, план запроса, спросить у коллег. Иначе 2 часа могут растянуться и на подольше🥺
#собес
Это вопрос с собесов, и тут главное - не перечислить все варианты, а показать, что вы умняши и соображаете
- появилось в разы больше данных
- параллельно с таблицей что-то происходит, и она залочена
- меньше ресурсов
- поменялись индексы
- поменялся план запроса (например, нужно помочь и добавить хинты)
- сетевые проблемы
- конфигурационные изменения (админы взяли и уменьшили параметры)
#собес
Please open Telegram to view this post
VIEW IN TELEGRAM
💯12🔥5⚡2
Смотрим на картинку.
В первой табличке хранятся приходящие события в разбивке по типу. Во второй - что мы должны получить в итоге.
Теперь мы хотим определить, когда каждое из типов событий началось, а когда закончилось. К примеру, 5го января пошло событие 2, и мы считаем, что это конец события 1.
#собес
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5
Я предлагаю такое решение:
SELECT
event_type,
date AS date_start,
LEAD(date) OVER(ORDER BY date) AS date_end
FROM (
SELECT
event_type,
date,
LAG(event_type, 1, -1) OVER(ORDER BY date) AS prev_event_type
FROM events) t
WHERE event_type != prev_event_type;
Что происходит?
LAG - предыдущий элемент, LEAD - последующий.offset = 1 - берем предыдущий элемент, c 2 был бы предпредыдущий и т.д.default_value = -1 - применится для самой первой строки, т.к. еще не с чем сравнивать. Отрицательные события вряд ли будут, поэтому значение кажется безопасным для любой выборки.После этого этапа в данных лежит:
event_type date prev_event_type
1 2024-01-01 -1
2 2024-01-05 1
1 2024-01-08 2
...
Готово!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤🔥2❤2
Как считать датасет в Spark?
Все, мы импортировали все, что нужно, создали сессию, нашли датасет, готовы работать!
Самые популярные способы:
⭐ csv
На месте path может быть 'file.csv', несколько файлов 'file1.csv,file2.csv' или вся папка целиком 'myfiles/csv_data'.
Из опций основные:
🐱
🐱
🐱
На больших данных использовать
Почему? Потому что спарк пойдет сканировать каждую строчку, чтобы определить корректный тип. Допустим, есть колонка age, заполненная числами типа int, но в сааамом конце у нас все поехало и попало число типа double. В конечном итоге столбец будет тоже иметь тип double, но для этого мы просканировали все на всякий случай. И так с каждым полем. Ну такое😕
Поэтому надо самим задать схему и указать ее при чтении файла:
⭐ json, parquet, hive-таблица
У нас есть HDFS (Hadoop Distributed File System) - это распределенная файловая система, в которой обычно хранятся большие данные. Там они лежат в виде файликов внутри папочек. А Hive - это обертка, чтобы мы могли читать данные из файликов через sql-запросы🐝
#spark
Все, мы импортировали все, что нужно, создали сессию, нашли датасет, готовы работать!
Самые популярные способы:
df = (
spark.read
.option('header', True)
.option('delimiter', ';')
.option('inferSchema', False)
.csv(path)
)
val df = spark.read
.option("header", "true")
.option("delimiter", ";")
.option("inferSchema", "false")
.csv(path)
На месте path может быть 'file.csv', несколько файлов 'file1.csv,file2.csv' или вся папка целиком 'myfiles/csv_data'.
Из опций основные:
header - заголовокdelimiter - разделитель полейinferSchema - автоматический определитель типов полейНа больших данных использовать
inferSchema опасно!Почему? Потому что спарк пойдет сканировать каждую строчку, чтобы определить корректный тип. Допустим, есть колонка age, заполненная числами типа int, но в сааамом конце у нас все поехало и попало число типа double. В конечном итоге столбец будет тоже иметь тип double, но для этого мы просканировали все на всякий случай. И так с каждым полем. Ну такое
Поэтому надо самим задать схему и указать ее при чтении файла:
table_schema = StructType([
StructField('name', StringType(), True), # True - допустимы ли null
StructField('age', IntegerType(), True),
...
])
df = (
spark.read
.option(...)
.schema(table_schema)
.csv(path)
)
StructType - класс для структуры датафрейма. StructField - для поля. Лежат в spark.sql.types.df = spark.read.json(path)
df = spark.read.parquet(path)
df = spark.table('db_schema.table_name')
У нас есть HDFS (Hadoop Distributed File System) - это распределенная файловая система, в которой обычно хранятся большие данные. Там они лежат в виде файликов внутри папочек. А Hive - это обертка, чтобы мы могли читать данные из файликов через sql-запросы
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5⚡2💯1
В таблице test есть поле dt с датой и временем в виде строки. Будет ли разница в запросах?
1. SELECT MAX(dt::date) FROM test;
2. SELECT MAX(dt)::date FROM test;
1. SELECT MAX(dt::date) FROM test;
2. SELECT MAX(dt)::date FROM test;
Anonymous Quiz
36%
да, первый запрос отработает быстрее
51%
да, второй запрос отработает быстрее
13%
нет, запросы одинаковы
🔥8
К чему был предыдущий вопрос?
🧳 На днях у нас с коллегой возник такой кейс: есть 50 таблиц, содержащих колонку с датой, но там есть дата, дата и время и unix timestamp. Цель - нужно их все объединить. БД - ClickHouse.
Итерация 1:
Количество считанных строк - 46 млрд. Время работы - 2,5 минуты. Ну... долго ждать, давайте еще раз внимательно посмотрим🤓
Итерация 2 - вынесли приведение даты со временем к дате в самый конец:
Количество считанных строк - 46 млрд. Время работы - 1,5 минуты. Уже лучше, но можно ли еще быстрее?
Итерация 3 - вынесли приведение unix timestamp к дате со временем:
Количество считанных строк - 25 млн. Время работы - 6 секунд. Супер! Нам этом остановимся👍
В итоге мы сначала находим максимальные значения и только потом кастуем. Выигрыш во времени - в 25 раз.
🕒 Время и строки можно посмотреть в системной табличке
🐢 В первых двух случаях в плане запроса есть шаги:
🐆 В третьем:
👍
#sql_tips
Итерация 1:
SELECT
max(
if(
table_name LIKE 'offline%',
toDate(FROM_UNIXTIME(dateTime::INT)),
toDate(dateTime)
)
) AS max_date
FROM schema.source_table
Количество считанных строк - 46 млрд. Время работы - 2,5 минуты. Ну... долго ждать, давайте еще раз внимательно посмотрим
Итерация 2 - вынесли приведение даты со временем к дате в самый конец:
SELECT
toDate(
max(
if(
table_name LIKE 'offline%',
FROM_UNIXTIME(dateTime::INT),
dateTime
)
)
) AS max_date
FROM schema.source_table
Количество считанных строк - 46 млрд. Время работы - 1,5 минуты. Уже лучше, но можно ли еще быстрее?
Итерация 3 - вынесли приведение unix timestamp к дате со временем:
SELECT
toDate(
if(
table_name LIKE 'offline%',
FROM_UNIXTIME(max(dateTime)::INT),
max(dateTime)
)
) AS max_date
FROM schema.source_table
Количество считанных строк - 25 млн. Время работы - 6 секунд. Супер! Нам этом остановимся
В итоге мы сначала находим максимальные значения и только потом кастуем. Выигрыш во времени - в 25 раз.
system.query_log в столбцах query_duration_ms, read_rows.ReadFromMergeTree (schema.source_table) - читаем таблицуExpression ((Conversion before UNION + (Projection + Before ORDER BY))) - кастуем к дате в каждом подзапросеReadFromStorage (MergeTree(with Aggregate projection _minmax_count_projection)) - вот эта проекция сильно быстрее, чем скан всей таблицы, а лишних приведений уже нет. Profit#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👏6❤🔥1💯1🆒1
У нас есть список, кортеж, что угодно, и нам нужно достать оттуда элементы:
person = (
'Eveline',
(1990, 12, 31),
'Moscow',
'+79271111111',
'+79272222222'
)
# Вместо того, чтобы обращаться по индексу:
name, birth_date, city, phone1, phone2 = person[0], person[1], person[2], person[3], person[4]
# можем сразу распаковать вот так:
name, birth_date, city, phone1, phone2 = person
Тогда в name пойдет имя, в birth_date - дата рождения и т.д.
Самое главное - количество переменных слева и элементов в объекте справа должно совпадать.
# Хочу скипнуть некоторые поля:
name, _, city, phone1, _ = person
# Хочу сложить телефоны в список:
name, birth_date, city, *phone_list = person
# список может идти в начале и середине тоже
# Хочу взять имя и phone2:
name, *_, phone2 = person
# Хочу взять только имя:
name, *_ = person
# Хочу взять только phone2:
*_, phone2 = person
# Хочу взять имя и год рождения:
name, (year, *_), *_ = person
#python_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18❤2⚡1
Как переименовать базу данных?
У меня часто бывали случаи, когда нужно было переименовать базу данных, но она не переименовывалась. В том проекте базу каждый сам создавал под свою задачу, поэтому операции с ней ни на кого не влияли.
Что нужно сделать?
Диалект - MS SQL Server
#sql_tips
У меня часто бывали случаи, когда нужно было переименовать базу данных, но она не переименовывалась. В том проекте базу каждый сам создавал под свою задачу, поэтому операции с ней ни на кого не влияли.
Что нужно сделать?
Диалект - MS SQL Server
--1. Переключиться в другую базу
USE master;
GO
--2. Установить однопользовательский режим
ALTER DATABASE [old_database] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
--3. Поменять название
ALTER DATABASE [old_database] MODIFY NAME = [new_database];
GO
--4. Восстановить режим
ALTER DATABASE [new_database] SET MULTI_USER;
GO
#sql_tips
🔥15🌚1
Как создать пустую таблицу из другой?
Бывают кейсы, когда есть табличка, но нам нужна пустая на ее основе.
Есть два варианта, которые будут работать везде:
1️⃣ TOP/LIMIT
2️⃣ заведомо ложное условие
#sql_tips
Бывают кейсы, когда есть табличка, но нам нужна пустая на ее основе.
Есть два варианта, которые будут работать везде:
SELECT *
INTO new_table
FROM old_table
LIMIT 0
SELECT TOP 0 *
INTO new_table
FROM old_table
SELECT *
INTO new_table
FROM old_table
WHERE 1=0
#sql_tips
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥10👻4
В спарке есть два типа операций: действия и трансформации.
Как и все мы, спарк очень любит лениться и ничего не делать. И он не будет тратить свою энергию на то, что никогда не понадобится. Так вот трансформации будут складироваться в цепочку по порядку, но никогда не выполнятся, если не будет запущено действие.
select() - выбираем столбцыwithColumn() - добавляем новое полеwhere()/filter() - фильтруемorderBy()/sort() - сортируемjoin() - джойнимgroupBy() - группируемunion() - объединяемdistinct() - удаляем дубликаты по всем столбцамdropDuplicates() - удаляем дубликаты по некоторым столбцамdrop() - удаляем столбцыSQL на максималках
show() - выводим датафрейм на экранcount() - считаем количество строкtoPandas() - собираем датафрейм в pandas dfКак использовать?
# Трансформации возвращают датафрейм
# Результата на экране нет
df = df.select('col1', 'col2').sort('col1')
# Действия возвращают НЕ датафрейм
# Результат на экране есть
df.show()
#spark
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👏2