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

Добавится ли запись в БД, если использовать запрос INSERT и EXPLAIN ANALYZE

2.2 Middle🔥 151 комментариев
#Базы данных (SQL)

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

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

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

Добавится ли запись в БД при использовании INSERT и EXPLAIN ANALYZE?

Это хороший вопрос о понимании SQL и транзакций. Ответ: ДА, запись добавится, и это может быть проблемой!

Основной момент

Эксплейнант (EXPLAIN ANALYZE) в PostgreSQL выполняет запрос полностью, включая побочные эффекты (INSERT, UPDATE, DELETE):

-- ❌ ЭТО ДОБАВИТ ЗАПИСЬ В БД!
EXPLAIN ANALYZE INSERT INTO users (name, email) VALUES ('John', 'john@test.com');

-- Результат: 1 новая запись в таблице users

Почему это происходит

Виды EXPLAIN в PostgreSQL:

-- 1. EXPLAIN (без выполнения) — БЕЗОПАСНО
EXPLAIN INSERT INTO users VALUES ('John', 'john@test.com');
-- Результат: план выполнения БЕЗ побочных эффектов

-- 2. EXPLAIN ANALYZE (с выполнением) — ОПАСНО!
EXPLAIN ANALYZE INSERT INTO users VALUES ('John', 'john@test.com');
-- Результат: план выполнения + реальное добавление записи!

-- 3. EXPLAIN ANALYZE внутри транзакции с ROLLBACK — относительно БЕЗОПАСНО
BEGIN;
EXPLAIN ANALYZE INSERT INTO users VALUES ('John', 'john@test.com');
ROLLBACK;  -- Отменяет все изменения

Демонстрация проблемы

import psycopg2
from psycopg2 import sql

def dangerous_example():
    conn = psycopg2.connect("dbname=test user=postgres")
    cursor = conn.cursor()
    
    # Проверяем начальное количество
    cursor.execute("SELECT COUNT(*) FROM users")
    initial_count = cursor.fetchone()[0]  # Например: 10
    print(f"Initial count: {initial_count}")
    
    # ОБЪЕКТ: используем EXPLAIN ANALYZE
    cursor.execute("""
        EXPLAIN ANALYZE 
        INSERT INTO users (name, email) 
        VALUES ('John', 'john@test.com')
    """)
    
    # Выполненный запрос добавит запись!
    conn.commit()
    
    # Проверяем результат
    cursor.execute("SELECT COUNT(*) FROM users")
    final_count = cursor.fetchone()[0]  # Теперь: 11
    print(f"Final count: {final_count}")
    
    cursor.close()
    conn.close()

# Результат: запись добавится, счётчик увеличится с 10 на 11

Корректный подход

1. Использовать EXPLAIN БЕЗ ANALYZE для проверки плана:

-- БЕЗОПАСНО: только посмотрим план, запись не добавится
EXPLAIN INSERT INTO users (name, email) VALUES ('John', 'john@test.com');

Презультат:
┌──────────────────────────────────────────────────────────────┐
│ Insert on users  (cost=0.01..0.02 rows=1 width=32)         │
│   ->  Result  (cost=0.01..0.02 rows=1 width=32)            │
│         One-Time Filter: true                               │
└──────────────────────────────────────────────────────────────┘

2. Использовать EXPLAIN ANALYZE внутри ROLLBACK:

import psycopg2

def safe_example():
    conn = psycopg2.connect("dbname=test user=postgres")
    cursor = conn.cursor()
    
    try:
        cursor.execute("BEGIN")
        
        # Смотрим план И выполняем
        cursor.execute("""
            EXPLAIN ANALYZE 
            INSERT INTO users (name, email) 
            VALUES ('John', 'john@test.com')
        """)
        
        # Получаем результаты
        result = cursor.fetchall()
        print("Plan with actual execution:")
        for row in result:
            print(row)
        
        # ОТКАТЫВАЕМ всё
        cursor.execute("ROLLBACK")
        print("\nTransaction rolled back - no data inserted")
        
    finally:
        cursor.close()
        conn.close()

Таблица отличий

КомандаВыполняет запросПобочные эффектыБезопасна
EXPLAIN INSERT...❌ Нет❌ Нет✅ Да
EXPLAIN ANALYZE INSERT...✅ Да✅ Да❌ Нет
EXPLAIN ANALYZE в BEGIN...ROLLBACK✅ Да✅ Да, но откатаны✅ Да

Практическое применение

Сценарий 1: Проверка производительности нового индекса

def check_performance():
    conn = psycopg2.connect("dbname=test user=postgres")
    cursor = conn.cursor()
    
    try:
        cursor.execute("BEGIN")
        
        # Создаём индекс
        cursor.execute("CREATE INDEX idx_email ON users(email)")
        
        # Проверяем план запроса с ANALYZE
        cursor.execute("""
            EXPLAIN (ANALYZE, BUFFERS)
            SELECT * FROM users WHERE email = 'john@test.com'
        """)
        
        # Смотрим результаты
        plan = cursor.fetchall()
        for line in plan:
            print(line[0])
        
        # Откатываем индекс (и другие изменения)
        cursor.execute("ROLLBACK")
        
    finally:
        cursor.close()
        conn.close()

Сценарий 2: Безопасная проверка плана INSERT

def safe_check_insert_plan():
    conn = psycopg2.connect("dbname=test user=postgres")
    cursor = conn.cursor()
    
    # Вариант 1: EXPLAIN БЕЗ ANALYZE (быстро, безопасно)
    cursor.execute("""
        EXPLAIN INSERT INTO users (name, email) 
        VALUES ('John', 'john@test.com')
    """)
    plan = cursor.fetchall()
    print("Simple plan:")
    for row in plan:
        print(row[0])
    
    # Вариант 2: EXPLAIN ANALYZE внутри ROLLBACK (точно, безопасно)
    cursor.execute("BEGIN")
    cursor.execute("""
        EXPLAIN ANALYZE INSERT INTO users (name, email) 
        VALUES ('John', 'john@test.com')
    """)
    plan = cursor.fetchall()
    cursor.execute("ROLLBACK")
    
    print("\nDetailed plan with actual execution (rolled back):")
    for row in plan:
        print(row[0])
    
    cursor.close()
    conn.close()

Ответ на собеседовании

Краткая версия:

Вопрос: Добавится ли запись при INSERT + EXPLAIN ANALYZE?
Ответ: ДА, добавится!

Почему:
- EXPLAIN ANALYZE ВЫПОЛНЯЕТ запрос полностью
- Это включает все побочные эффекты (INSERT, UPDATE, DELETE)
- Запись будет добавлена и закоммичена

Как избежать:
- Использовать EXPLAIN БЕЗ ANALYZE (безопасно, но менее точно)
- Использовать EXPLAIN ANALYZE в BEGIN...ROLLBACK (точно и безопасно)
- Никогда не использовать EXPLAIN ANALYZE на production прямо так

Расширенная версия:

Это показывает понимание:

  • Различия между EXPLAIN и EXPLAIN ANALYZE
  • Побочных эффектов SQL команд
  • Транзакций и ROLLBACK
  • Важности осторожности на production

Важное замечание

⚠️ На production сервере:

# ❌ НИКОГДА так не делай!
cursor.execute("EXPLAIN ANALYZE INSERT INTO production_table VALUES (...)")
conn.commit()  # Запись добавится!

# ✅ Всегда используй:
cursor.execute("BEGIN")
cursor.execute("EXPLAIN ANALYZE INSERT INTO table VALUES (...)")
cursor.execute("ROLLBACK")  # Откатываем

Это критическое знание для разработчика, так как случайный EXPLAIN ANALYZE на production может повредить данные.