Периодически в коде запросов и "заточенных" под них индексов наблюдаю примерно подобные куски:
coalesce("Фамилия", '') || ' ' || coalesce("Имя", '') || ' ' || coalesce("Отчество", '')
Понятно, что тут хотели обезопасить себя от заполненности любого из полей NULL-значением, чтобы случайно вся строка не заNULL'илась.
Правда, тут возникают некоторые артефакты в виде "висящих пробелов" типа
' Иван Иванович'
или 'Иванов Иван '
.Но ведь есть решение изящнее и проще - функция concat_ws:
concat_ws(' ', "Фамилия", "Имя", "Отчество")
RTFM!
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23🔥10❤5👎1
├── Пересмотр покрывающих индексов
├── Частичные индексы для отфильтрованных данных
└── Стратегии индексирования для соединений
├── Пересмотр подзапросов к JOIN'ам
├── Общие табличные выражения (CTE) и рекурсивные запросы
└── Ограничение наборов результатов с помощью OFFSET-FETCH
├── Выбор кандидатов на денормализацию
├── Создание денормализованных таблиц
└── Баланс между согласованностью данных и производительностью
├── Временные таблицы для промежуточных результатов
├── Создание и наполнение временных таблиц
└── Временные таблицы и агрегации
├── Оптимизация подзапросов
└── Оптимизация агрегаций
├── Операции на основе множеств
├── Пакетная обработка
└── Оптимизация циклов с помощью операций на основе множеств
Примеры некоторых из этих приёмов
Пересмотр подзапросов к JOIN'ам.
Оригинальный подзапрос:
SELECT * FROM employees
WHERE department_id IN (SELECT department_id FROM departments WHERE region = 'West');
Переписан с использованием JOIN:
SELECT e.* FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
WHERE d.region = 'West';
Общие табличные выражения (CTE) предоставляют удобный способ упростить сложные запросы и улучшить их читаемость:
WITH RecursiveCTE AS (
SELECT employee_id, first_name, manager_id, 0 AS depth
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.employee_id, e.first_name, e.manager_id, rc.depth + 1
FROM employees e
INNER JOIN RecursiveCTE rc ON e.manager_id = rc.employee_id
)
SELECT * FROM RecursiveCTE;
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍27❤5🔥3🤔1
💎 Шпаргалки для всех: сокровищница знаний
Этот ресурс - настоящее сокровище для любого разработчика.
В нем содержится около 6000 разнообразных шпаргалок в виде изображений: от программирования и работы с базами данных до софт-скиллов.
Здесь вы найдете не только классические шпаргалки по регулярным выражениям и SQL, но и информацию о кофейных напитках, командах в Minecraft и французской грамматике.
▪ Смотреть
@sqlhub
Этот ресурс - настоящее сокровище для любого разработчика.
В нем содержится около 6000 разнообразных шпаргалок в виде изображений: от программирования и работы с базами данных до софт-скиллов.
Здесь вы найдете не только классические шпаргалки по регулярным выражениям и SQL, но и информацию о кофейных напитках, командах в Minecraft и французской грамматике.
▪ Смотреть
@sqlhub
❤11👍6🔥2
Для того чтобы проверить наличие записи, используйте оператор
EXISTS
:IF EXISTS (SELECT 1 FROM ваша_таблица WHERE ваш_столбец = ваше_значение)
PRINT 'Запись существует';
ELSE
PRINT 'Запись не найдена';
Замените
ваша_таблица
, ваш_столбец
и ваше_значение
релевантными данными. Этот способ является эффективным для поиска записей.SELECT COUNT(*)
для проверки наличия записи может показаться удобным, однако это не всегда эффективный подход, особенно при работе с обширными базами данных.В MS SQL Server целесообразно использовать
SELECT TOP 1
для сохранения ресурсов:IF EXISTS (SELECT TOP 1 1 FROM ваша_таблица WHERE ваш_столбец = ваше_значение)
PRINT 'Запись существует';
В MySQL и PostgreSQL применяйте
LIMIT 1
для ограничения выборки:SELECT 1 FROM ваша_таблица WHERE ваш_столбец = ваше_значение LIMIT 1;
Такие запросы быстро останавливают выполнение, как только найдено первое соответствие.
SELECT COUNT(*)
. Несмотря на то что многие СУБД оптимизируют SELECT COUNT(*)
, в работе не все из них показывают одинаковую эффективность. В случае сомнений используйте EXISTS
или LIMIT/TOP
.@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍27🔥6❤3
PostgreSQL 16 вносит немало улучшений в планировщик запросов и позволяет выполнять многие SQL-запросы быстрее, чем в предыдущих версиях PostgreSQL.
Вот некоторые из изменений:
DISTINCT
ORDER BY
или DISTINCT
, использовать предварительно отсортированные данныеUNION ALL
anti-join
с ненулевым входом в качестве внутреннего отношенияFULL
и RIGHT OUTER
хэш-соединенияROWS
, когда режим RANGE
активен, но не нуженntile()
, cume_dist()
и percent_rank()
left join
и unique joins
в секционированных таблицахLimit
вместо Unique
для реализации DISTINCT
, когда это возможноselect_outer_pathkeys_for_merge()
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍8🔥3🤔2
Если в двух словах
Выбор между
BYTE
и CHAR
зависит от кодировки и объема данных, которые вы собираетесь представлять в байтах. BYTE
фиксирует объем данных в байтах и идеален для однобайтовых кодировок, таких как ASCII
где каждому символу соответствует один байт. Так, VARCHAR2(10 BYTE)
вмещает любую комбинацию из десяти байтов. CHAR
же определяет количество символов, не учитывая их объем в байтах; выбрав VARCHAR2(10 CHAR)
, вы гарантируете пространство на 10 символов, независимо от их размера в байтах. Для иллюстрации вот простой SQL-пример:-- BYTE: сохраняет байты
CREATE TABLE bytewise (name VARCHAR2(10 BYTE));
-- CHAR: распределяет пространство с учетом количества символов
CREATE TABLE charwise (name VARCHAR2(10 CHAR));
Если
BYTE
обеспечивает компактность данных, то CHAR
гарантирует гибкость, позволяя сохранять символы различной ширины. Ваш выбор зависит от типов символов, которые вы планируете использовать.Детали при работе с UTF-8
Важно понимать последствия выбора типа данных при работе с многобайтовыми символами кодировки, такой как UTF-8, где один символ может занимать до 4 байтов.
BYTE
, это может привести к проблемам в системах, поддерживающих много языков. CHAR
будет более надежной опцией, так как он обеспечивает достаточное пространство для любого символа, независимо от его веса.Критерии для выбора между
BYTE
и CHAR
:BYTE
будет наилучшим выбором.CHAR
выигрывает, когда важна поддержка множества языков, это помогает предотвратить проблемы с несоответствием размера хранения.BYTE
может быть быстрее по сравнению с CHAR
из-за обработки меньшего объема данных, что делает его подходящим для приложений с высокими требованиями к производительности.BYTE
и CHAR
можно прочитать тут @sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤3👎1🔥1
Вроде мелочь, а вполне-таки реальный кейс
В общем, необходимо вносить в договор дату в формате: "30 мая 2099 г."
Эта дата хранится в БД, ее нужно просто подгрузить и нормально отформатировать.
Для этого можно обращаться к месяцу с помощью:
LOWER(DATENAME(month, @Tdate))
, однако в таком случае получается месяц в именительном падеже - "май".И тут полезно вспомнить, что форматирование даты с правильными склонениями месяца работает из коробки:
SELECT FORMAT(GETDATE(), 'dd MMMM yyyy', 'ru-RU')
-- 30 мая 2099
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍33🔥11❤8🤯1
concurrently control
. При том, не только в БД, а везде где хоть что-то выполняется параллельно.Допустим есть пользователь, у него есть 100 денег на счету. Пользователь может их тратить, вы проверяете баланс
SELECT balance ...
, затем обновляете баланс при покупке UPDATE ... SET balance = ? WHERE ...
. И вот в счастливый день как-то так вышло, что приходят сразу 2 запроса на покупки для этого пользователя. Одна на 50 денег, вторая на 70. Одна из них должна быть отклонена, т.к. денег недостаточно. Но в результате получается что обе покупки прошли и у вас проблема, вы продали то, что не надо было. И это даже не видно по балансу пользователя. Как же?Это типичный
race condition
, обе транзакции сначала данные читают, потом локально что-то делают, потом что-то пишут.При конкурентном доступе к ресурсу подрались только на последнем шаге, транзакция которая начала обновлять данные позже сначала подождала завершение первой транзакции. А затем банально перезаписала баланс на тот который считала правильным сама —
lost update
аномалия.FOR SHARE
, FOR UPDATE
дополнения. Потому они кстати и задокументированы в разделе Explicit Locking@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
❤17👍9🔥4👎1
SQL_cheatsheet.png
971 KB
Шпаргалок по SQL много не бывает, так что держите, полезно освежить перед собеседованием
Здесь освещаются самые основные операторы, такие как
JOIN
, IN
, LIKE
, BETWEEN
, ORDER BY
, а также много всего ещёПользуйтесь)
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20❤10👍2🎉1
В рейтинг попали самые перспективные разработки в сфере генеративного искусственного интеллекта по ключевым категориям. Туда попали сразу две нейросетки от Яндекса — текстовая YandexGPT и мультимодальная YandexART. Также Яндекс вместе с Stability AI, Open AI, Google, Microsoft, Tencent и Baidu стал одной из 11 компаний со всего мира, которые разрабатывают более одного типа GenAI-моделей.
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤5🔥3🤬1
Состоялся релиз мажорной версии открытого масштабируемого решения для кластеризации баз данных MySQL — Vitess 19.
В этой версии разработчики добавили улучшения, направленные на оптимизацию масштабируемости, производительности и удобства использования с базами данных.
Изменения и дополнения в Vitess 19:
«Следуя тенденции последних трёх лет, новая версия Vitess быстрее предыдущей во всех тестах, которые мы отслеживаем в Arewefastyet. Мы исправили несколько проблем с производительностью, доработали интерфейс и код», — пояснили разработчики, порекомендовав изучить документацию проекта и список исправлений.
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤2🔥1
Как правильно подставлять значения из переменных / списков / словарей в SQL запрос в Python?
⏩ Лучше всего использовать SQL запросы с использованием параметров:
⏩ Какие преимущества приносит использование параметров?
— Защита от SQL-инъекций
— Правильное квотирование литералов в зависимости от их типа (пример со строками, пример с датами).
— Оптимизация — сокращение времени работы SQL запроса. Благодаря использованию параметров следующие шаги не выполняются при повторном запуске (зависит от БД):
— проверка синтаксиса SQL запроса
— проверка прав доступа к объектам БД
— построение плана выполнения SQL запроса
— Защита от переполнения/вытеснения кеша SQL запросов. Например "безобидный" запрос
⏩ Пример использования параметров в SQL запросе:
При таком подходе можно использовать
📎 Читать подробнее
@sqlhub
cursor.execute("SELECT * FROM my_table WHERE id = ?", [123])
# parameter placeholder ------------------------> ^
# список/кортеж со значениями параметров -----------> ^^^^^
— Защита от SQL-инъекций
— Правильное квотирование литералов в зависимости от их типа (пример со строками, пример с датами).
— Оптимизация — сокращение времени работы SQL запроса. Благодаря использованию параметров следующие шаги не выполняются при повторном запуске (зависит от БД):
— проверка синтаксиса SQL запроса
— проверка прав доступа к объектам БД
— построение плана выполнения SQL запроса
— Защита от переполнения/вытеснения кеша SQL запросов. Например "безобидный" запрос
qry = f"SELECT first_name, last_name FROM users WHERE id = {user_id}"
, который часто выполняется в нагруженной системе с различными значениями user_id
может вытеснить из кеша запросов полезные запросы. import sqlite3
con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("create table lang (name, first_appeared)")
cur.execute("insert into lang values (?, ?)", ("C", 1972))
lang_list = [
("Fortran", 1957),
("Python", 1991),
("Go", 2009),
]
cur.executemany("insert into lang values (?, ?)", lang_list)
cur.execute("select * from lang where first_appeared=:year", {"year": 1972})
print(cur.fetchall())
con.close()
При таком подходе можно использовать
cursor.executemany()
- это значительно быстрее и эффективнее по сравнению с вставкой в цикле по одной строке.@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤3🔥2😁1
BEGIN
, COMMIT
, ROLLBACK
.BEGIN
и завершается командой COMMIT
(успешное завершение) или ROLLBACK
(откат). Указывать BEGIN
, COMMIT
и ROLLBACK
не обязательно, часто их использование подразумевается неявно. В случае если сессия неожиданно прерывается, тогда все транзакции, которые были начаты в текущей сесcии – автоматически откатываются.—
BEGIN
– инициирует новую транзакцию. После выполнения этой команды все последующие операции с базой данных будут выполняться в рамках этой транзакции.—
COMMIT
– завершает текущую транзакцию, применяя все её операции. Если все операции в транзакции были успешными, результаты этих операций фиксируются (становятся постоянными). Изменения становятся видны последующим транзакциям.—
ROLLBACK
– откатывает текущую транзакцию, отменяя все её операции, если в процессе выполнения транзакции возникли ошибки или отмена транзакции производится приложением исходя из внутренней логики работы.COMMIT;
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25❤4🔥3
- Проблема: Выбор всех столбцов с помощью
SELECT *
приводит к передаче ненужных данных, увеличению использования памяти и снижению производительности запросов.- Решение: Укажите в операторе SELECT только необходимые столбцы.
-- Пример проблемы
SELECT * FROM employees;
-- Улучшенный запрос
SELECT employee_id, first_name, last_name FROM employees;
- Проблема: Отсутствие индексов может привести к полному сканированию таблицы и снижению производительности запросов.
- Решение: Создайте и используйте индексы для часто используемых в выражениях WHERE столбцов.
-- Создание индекса
CREATE INDEX idx_last_name ON employees(last_name);
-- Использования индекса в запросе
SELECT * FROM employees WHERE last_name = 'Smith';
- Проблема: Подзапросы могут работать медленнее, чем JOIN, особенно при работе с большими наборами данных.
- Решение: Используйте JOIN, когда это возможно, а подзапросы оставьте для ситуаций, в которых они более эффективны.
-- Пример проблемы (подзапрос)
SELECT department_name FROM departments WHERE department_id IN (SELECT department_id FROM employees);
-- Улучшенный запрос (JOIN)
SELECT DISTINCT d.department_name FROM departments d JOIN employees e ON d.department_id = e.department_id;
- Проблема: Выбор неправильного типа JOIN (например, Cartesian JOIN) или неправильное указание условий соединения может привести к неправильным результатам или замедлению запросов.
- Решение: Разберитесь в различных типах JOIN (INNER, LEFT, RIGHT, FULL) и используйте их по назначению.
-- Пример проблемы (Cartesian JOIN)
SELECT * FROM employees, departments;
-- Улучшенный запрос (INNER JOIN)
SELECT e.employee_name, d.department_name FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;
- Проблема: Отсутствие фильтрации данных с помощью выражений WHERE может привести к запросу ненужных данных.
- Решение: Всегда включайте выражения WHERE, ограничивающие набор результатов.
-- Пример проблемы (без выражения WHERE)
SELECT * FROM orders;
-- Улучшенный запрос (с выражением WHERE)
SELECT * FROM orders WHERE order_date >= '2023-01-01';
- Проблема: Игнорирование планов выполнения запросов может привести к упущенным возможностям оптимизации.
- Решение: Используйте такие инструменты, как EXPLAIN, для анализа планов выполнения и внесения необходимых оптимизаций.
-- Просмотр плана выполнения
EXPLAIN SELECT * FROM products WHERE category = 'Electronics';
- Проблема: Запросы, хорошо работающие с небольшими наборами данных, могут плохо работать с большими объёмами данных.
- Решение: Реализуйте такие стратегии, как пагинация, разбиение данных на разделы и оптимизация индексов для больших наборов данных.
-- реализация пагинации
SELECT * FROM products LIMIT 10 OFFSET 20;
- Проблема: Повторение одних и тех же агрегаций в нескольких частях запроса может быть неэффективным.
- Решение: Используйте CTE (Общие табличные выражения) для хранения промежуточных результатов и избегайте лишних вычислений.
-- Пример проблемы (повторяющаяся агрегация)
SELECT department, SUM(salary) AS total_salary FROM employees GROUP BY department;
-- Улучшенный запрос (с CTE)
WITH DepartmentSalaries AS (
SELECT department, SUM(salary) AS total_salary FROM employees GROUP BY department
)
SELECT * FROM DepartmentSalaries;
- Проблема: Неправильная обработка ошибок может привести к сбоям в работе приложения или неправильным результатам.
- Решение: Реализуйте надлежащую обработку ошибок в SQL запросах или в коде приложения.
-- Пример обработки ошибок в SQL (MySQL)
BEGIN;
-- SQL выражение
IF some_condition THEN
ROLLBACK; -- Откат транзакции при ошибке
ELSE
COMMIT; -- Коммит транзакции при успешном выполнении всех выражений
END IF;
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍40❤9🔥5👎2
Представьте, что у вас есть несколько таблиц Postgres, с которыми вам нужно работать и обрабатывать данные из таблиц с помощью Pandas по расписанию.
Как вы можете этого добиться?
Kestra, orchestrator проект с открытым исходным кодом, который поможет вам без особых усилий организовать этот рабочий процесс, используя файл
https://pypi.org/project/kestra/
@sqlhub
Как вы можете этого добиться?
Kestra, orchestrator проект с открытым исходным кодом, который поможет вам без особых усилий организовать этот рабочий процесс, используя файл
YAML
.https://pypi.org/project/kestra/
@sqlhub
❤10🔥3🤔2👍1
Временные таблицы в SQL обеспечивают удобный способ разбиения сложных задач на более мелкие и управляемые. Они позволяют разделить этапы обработки данных, что может повысить производительность, улучшить читаемость кода и упростить логику запросов.
CREATE TABLE
с ключевым словом TEMPORARY
или TEMP
перед именем таблицы:CREATE TEMPORARY TABLE temp_table (
id INT,
name VARCHAR(50),
age INT
);
Что тут происходит:
— Инструкция
CREATE TEMPORARY TABLE
используется для создания временной таблицы.—
temp_table
— это имя, которое присваивается временной таблице. Имя можно выбрать любое.— Внутри круглых скобок мы определяем столбцы временной таблицы.
— В данном примере временная таблица
temp_table
имеет три столбца: id
типа INT
, name
типа VARCHAR(50)
и age
типа INT
.— При необходимости мы можем добавить дополнительные столбцы, указав их имена и типы данных.
— Временная таблица автоматически удаляется в конце сеанса или при завершении сеанса.
Допустим, у нас есть большой набор данных, и мы хотим выполнить сложный анализ или вычисления на меньшей части этих данных. Для такого анализа можно создать временную таблицу, содержащую только необходимые строки и столбцы.
-- Создать временную таблицу с подмножеством данных
CREATE TEMPORARY TABLE subset_data AS
SELECT column1, column2, column3
FROM original_table
WHERE condition;
-- Анализ подмножества данных
SELECT column1, AVG(column2) AS average_value
FROM subset_data
GROUP BY column1;
-- Удалить временную таблицу
DROP TABLE subset_data;
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤16👍9🔥3
💎 Квантизация нейросетевых моделей
Интересная статья, в которой ML-разработчик Яндекса рассказывает, что такое квантизация и как она помогает экономить ресурсы, разбирает типы данных и объясняет, что квантизовать, чтобы улучшить эффективность модели.
Автор также рассказывает, что такое выбросы, как они портят жизнь ML-инженерам, а также как с ними бороться.
📎 Статья
@sqlhub
Интересная статья, в которой ML-разработчик Яндекса рассказывает, что такое квантизация и как она помогает экономить ресурсы, разбирает типы данных и объясняет, что квантизовать, чтобы улучшить эффективность модели.
Автор также рассказывает, что такое выбросы, как они портят жизнь ML-инженерам, а также как с ними бороться.
@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍3😁2🔥1
В статье показано, как с помощью matching в Oracle #SQL найти серию последовательных строк с начальной даты и до конечной:
MATCH_RECOGNIZE (
...
PATTERN (str e+)
DEFINE e AS start_date = PREV(end_date) + 1 --start is 1 day after previous end
)
https://how2ora-en.blogspot.com/2023/01/how-to-check-if-row-is-continuation-of.html@sqlhub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤3🔥2