1.3Kпросмотров
30.0%от подписчиков
10 февраля 2026 г.
Score: 1.5K
🔌 Python + SQLite: скрытые возможности (возврат изменённых строк, оконные функции) Все пишут SELECT и INSERT, но SQLite — это полноценная СУБД с фичами из больших баз. Используй RETURNING, UPSERT и оконные функции, чтобы логика работала на уровне данных, а не в коде. ⚡️ UPSERT: вставь или обнови одной командой Классика: нужно обновить запись, если она есть, иначе создать. Раньше требовалось два запроса. Теперь — один:
import sqlite3 conn = sqlite3.connect(':memory:')
conn.execute('''CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT UNIQUE, login_count INTEGER)''') # Первый вызов — вставит новую запись
conn.execute('''INSERT INTO users (email, login_count) VALUES ('alice@mail.com', 1) ON CONFLICT(email) DO UPDATE SET login_count = login_count + 1''') # Второй вызов — увеличит счётчик
conn.execute('''INSERT INTO users (email, login_count) VALUES ('alice@mail.com', 1) ON CONFLICT(email) DO UPDATE SET login_count = users.login_count + 1''') result = conn.execute('SELECT FROM users').fetchone()
print(result) # (1, 'alice@mail.com', 2)
➡️ ON CONFLICT DO UPDATE — магия UPSERT. Ключевое поле должно иметь ограничение UNIQUE или PRIMARY KEY. ✅ RETURNING: возвращай изменённые строки сразу Раньше чтобы получить ID новой записи, делали отдельный SELECT. Теперь всё сразу:
# Возвращаем ID и данные новой записи
cursor = conn.execute('''INSERT INTO users (email, login_count) VALUES ('bob@mail.com', 1) RETURNING id, email''')
new_user = cursor.fetchone()
print(f'Новый ID: {new_user[0]}, email: {new_user[1]}') # Работает и с UPDATE/DELETE
cursor = conn.execute('''UPDATE users SET login_count = login_count 2 WHERE email = 'alice@mail.com' RETURNING email, login_count''')
updated = cursor.fetchone()
print(f'Обновлено: {updated[0]}, новые логины: {updated[1]}')
➡️ RETURNING возвращает строки, затронутые INSERT, UPDATE или DELETE. Идеально для логирования и цепочек операций. 🧪 Оконные функции (OVER): аналитика без костылей Нужно пронумеровать строки, посчитать скользящее среднее или ранжировать — делай одним запросом:
# Создадим таблицу с продажами
conn.execute('''CREATE TABLE sales ( id INTEGER PRIMARY KEY, product TEXT, amount REAL, sale_date DATE
)''') # Заполняем тестовыми данными
sales_data = [ ('Ноутбук', 1000, '2024-01-15'), ('Ноутбук', 1200, '2024-01-20'), ('Мышь', 50, '2024-01-10'), ('Мышь', 45, '2024-01-18'), ('Клавиатура', 80, '2024-01-12'),
]
conn.executemany('INSERT INTO sales (product, amount, sale_date) VALUES (?, ?, ?)', sales_data) # Ранжируем товары по убыванию суммы продаж внутри каждой категории
cursor = conn.execute(''' SELECT product, amount, sale_date, ROW_NUMBER() OVER (PARTITION BY product ORDER BY amount DESC) as rank_in_product, SUM(amount) OVER (PARTITION BY product) as total_per_product, AVG(amount) OVER (ORDER BY sale_date ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) as moving_avg FROM sales ORDER BY product, amount DESC
''') for row in cursor.fetchall(): print(row)
➡️ OVER (PARTITION BY ... ORDER BY ...) создаёт "окно" для вычислений. Не путать с GROUP BY — здесь строки не сворачиваются. 🗣 Запомни: Современный SQLite — это не просто хранилище. Используй UPSERT для атомарных обновлений, RETURNING чтобы не делать лишних запросов, а оконные функции — для аналитики прямо в БД. Понра