← Назад к вопросам

Сталкивался ли с нативным SQL

1.0 Junior🔥 201 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Да, я активно использую нативный 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, а необходимый инструмент в арсенале разработчика.