Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Да, я активно использую нативный SQL в своей практике, особенно в Node.js проектах. Это критически важный навык для backend-разработчика, несмотря на популярность ORM.
Когда использую нативный SQL
1. Сложные запросы и аналитика
Когда нужна оптимизация производительности, я пишу прямой SQL. ORM часто генерирует неэффективные запросы для сложных JOINов и агрегаций. Например, для отчёта по продажам с группировкой по датам и категориям:
SELECT
DATE_TRUNC(day, orders.created_at) as date,
categories.name,
COUNT(*) as order_count,
SUM(orders.total) as revenue,
AVG(orders.total) as avg_order
FROM orders
JOIN products ON orders.product_id = products.id
JOIN categories ON products.category_id = categories.id
WHERE orders.created_at >= NOW() - INTERVAL 30 days
GROUP BY DATE_TRUNC(day, orders.created_at), categories.id, categories.name
ORDER BY date DESC, revenue DESC;
ORM такой запрос либо не сгенерирует, либо сгенерирует N+1 проблему.
2. Миграции и схема БД
Использую инструменты типа Goose, Flyway для управления версиями схемы. Нативный SQL даёт полный контроль над индексами, констрейнтами и оптимизацией:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_created_at ON users(created_at DESC);
3. Batch-операции
Для обработки больших объёмов данных SQL эффективнее:
UPDATE orders
SET status = archived
WHERE created_at < NOW() - INTERVAL 90 days
AND status = completed;
В Node.js это вызываю через пулы подключений (node-postgres или similar).
Stack в Node.js
node-postgres (pg) — это мой выбор для прямого SQL:
const { Pool } = require(pg);
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
});
// Параметризованный запрос (защита от SQL-injection)
const user = await pool.query(
SELECT * FROM users WHERE id = $1,
[userId]
);
TypeScript + типизация результатов:
import { QueryResult } from pg;
interface User {
id: string;
email: string;
created_at: Date;
}
const result: QueryResult<User> = await pool.query(
SELECT id, email, created_at FROM users WHERE id = $1,
[userId]
);
const user = result.rows[0];
Гибридный подход
В реальных проектах комбинирую:
- ORM (TypeORM, Prisma) для CRUD операций — быстро и безопасно
- Нативный SQL для сложных запросов — контроль и производительность
- QueryBuilder ORM для промежуточных случаев
Например, с TypeORM:
// Простой CRUD через ORM
const user = await userRepository.findOne(id);
// Сложный запрос через Query Builder
const stats = await userRepository
.createQueryBuilder(user)
.select(user.created_at, date)
.addSelect(COUNT(*), count)
.groupBy(user.created_at)
.getRawMany();
// Супер-сложный запрос — прямой SQL
const custom = await connection.query(`
WITH ranked_users AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY created_at DESC) as rn
FROM users
)
SELECT * FROM ranked_users WHERE rn <= 10
`);
Что важно помнить
- Параметризованные запросы всегда — защита от SQL-injection
- N+1 проблема контролируется JOINами в одном запросе
- Индексы критичны для производительности
- EXPLAIN ANALYZE — друг для оптимизации
- Транзакции для консистентности данных
Нативный SQL — не конкурент ORM, а необходимый инструмент в арсенале разработчика.