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

Является ли первичный ключ проиндексированным в SQL?

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

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

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

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

Является ли первичный ключ проиндексированным в SQL?

Ответ: Да, первичный ключ (Primary Key) ВСЕГДА автоматически индексируется во всех основных системах управления базами данных (СУБД).

Это одна из основных характеристик первичного ключа и выполняется автоматически.

Почему первичный ключ индексируется

Первичный ключ по определению должен быть уникальным идентификатором каждой записи. Для обеспечения этого уникальности СУБД автоматически создаёт индекс, который:

  1. Гарантирует уникальность — поиск по первичному ключу должен быть быстрым и вернуть только одну запись
  2. Ускоряет поиск — индекс позволяет находить записи за O(log n) вместо O(n)
  3. Поддерживает целостность — предотвращает добавление дубликатов

SQL примеры

PostgreSQL

CREATE TABLE users (
    id SERIAL PRIMARY KEY,  -- Индекс создаётся АВТОМАТИЧЕСКИ
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE  -- Отдельный индекс для UNIQUE
);

-- Проверяем индексы
\d users

-- Результат:
-- Public | users_pkey | index | postgres | users | btree | id
-- Public | users_email_key | index | postgres | users | btree | email

Примечание: В PostgreSQL индекс для PRIMARY KEY создаётся с именем <table>_pkey.

MySQL

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,  -- Индекс АВТОМАТИЧЕСКИ
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE  -- Отдельный индекс
);

-- Проверяем индексы
SHOW INDEXES FROM users;

-- Результат:
-- users | 0 | PRIMARY | 1 | id | ... | BTREE |
-- users | 0 | email | 1 | email | ... | BTREE |

SQL Server

CREATE TABLE users (
    id INT PRIMARY KEY,  -- Индекс АВТОМАТИЧЕСКИ (CLUSTERED)
    name NVARCHAR(100) NOT NULL,
    email NVARCHAR(100) UNIQUE  -- Отдельный индекс
);

-- Проверяем индексы
SP_HELPINDEX 'users';

Типы индексов для первичного ключа

1. Clustered Index (по умолчанию в SQL Server)

Данные физически отсортированы по первичному ключу.

Данные в диске:
id=1 | name=Alice
id=2 | name=Bob
id=3 | name=Charlie
id=4 | name=David
-- SQL Server: PRIMARY KEY создаёт CLUSTERED индекс
CREATE TABLE users (
    id INT PRIMARY KEY,  -- Автоматический CLUSTERED индекс
    name VARCHAR(100)
);

Плюсы Clustered Index:

  • Поиск по PRIMARY KEY максимально быстрый
  • Последовательное сканирование быстрое

Минусы:

  • Переиндексирование дорого (нужно пересортировать данные)
  • Только один CLUSTERED индекс на таблицу

2. Non-Clustered Index (в PostgreSQL, MySQL)

Отдельная структура B-tree, которая указывает на данные.

Индекс (B-tree)          Данные на диске
id=1 ──────→ 0x1000 |  name=Alice
id=2 ──────→ 0x2000 |  name=Bob
id=3 ──────→ 0x3000 |  name=Charlie
-- PostgreSQL: PRIMARY KEY создаёт NON-CLUSTERED индекс
CREATE TABLE users (
    id SERIAL PRIMARY KEY,  -- Автоматический B-tree индекс
    name VARCHAR(100)
);

Производительность поиска

Без индекса (Full Table Scan)

SELECT * FROM users WHERE id = 100;

-- О(n) — проверяет все n записей
-- 1 млн записей = 1 млн операций

С индексом (Index Seek)

SELECT * FROM users WHERE id = 100;

-- O(log n) — использует индекс
-- 1 млн записей = ~20 операций (log2(1000000) ≈ 20)

Практический пример: Java с JDBC

import java.sql.*;

public class PrimaryKeyIndexExample {
    public static void main(String[] args) throws SQLException {
        String url = "jdbc:postgresql://localhost/mydb";
        String user = "postgres";
        String password = "password";
        
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            // 1. Создаём таблицу с PRIMARY KEY (индекс создаётся АВТОМАТИЧЕСКИ)
            String createTableSQL = """n                CREATE TABLE IF NOT EXISTS products (
                    product_id SERIAL PRIMARY KEY,
                    name VARCHAR(100) NOT NULL,
                    price DECIMAL(10, 2)
                )
            """;
            
            try (Statement stmt = conn.createStatement()) {
                stmt.execute(createTableSQL);
            }
            
            // 2. Вставляем данные
            String insertSQL = "INSERT INTO products (name, price) VALUES (?, ?)";
            try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {
                for (int i = 1; i <= 1000; i++) {
                    pstmt.setString(1, "Product " + i);
                    pstmt.setDouble(2, 10.0 + i);
                    pstmt.addBatch();
                }
                pstmt.executeBatch();
            }
            
            // 3. Поиск по PRIMARY KEY (использует индекс — быстро!)
            String selectSQL = "SELECT * FROM products WHERE product_id = ?";
            try (PreparedStatement pstmt = conn.prepareStatement(selectSQL)) {
                pstmt.setInt(1, 500);
                ResultSet rs = pstmt.executeQuery();
                
                while (rs.next()) {
                    System.out.println("ID: " + rs.getInt("product_id"));
                    System.out.println("Name: " + rs.getString("name"));
                    System.out.println("Price: " + rs.getDouble("price"));
                }
            }
            
            // 4. Проверяем индексы (PostgreSQL)
            String indexSQL = """
                SELECT indexname, indexdef 
                FROM pg_indexes 
                WHERE tablename = 'products'
            """;
            try (Statement stmt = conn.createStatement()) {
                ResultSet rs = stmt.executeQuery(indexSQL);
                while (rs.next()) {
                    System.out.println("Индекс: " + rs.getString("indexname"));
                    System.out.println("Определение: " + rs.getString("indexdef"));
                }
            }
        }
    }
}

Когда PRIMARY KEY индекс используется

-- ✓ ИСПОЛЬЗУЕТСЯ индекс (быстро)
SELECT * FROM users WHERE id = 100;
SELECT * FROM users WHERE id IN (1, 2, 3);
SELECT * FROM users WHERE id > 100 AND id < 200;
SELECT * FROM users ORDER BY id;  -- может использовать индекс
DELETE FROM users WHERE id = 100;  -- использует индекс
UPDATE users SET name='John' WHERE id = 100;  -- использует индекс

-- ✗ НЕ ИСПОЛЬЗУЕТСЯ индекс (может быть медленно)
SELECT * FROM users WHERE name = 'John';  -- нужен индекс на name
SELECT * FROM users WHERE UPPER(id) = '100';  -- функция на индексированном поле
SELECT * FROM users;  -- FULL TABLE SCAN

Проверка плана выполнения

-- PostgreSQL
EXPLAIN ANALYZE
SELECT * FROM users WHERE id = 100;

-- Результат:
-- Index Scan using users_pkey on users (cost=0.29..8.30 rows=1) ✓
-- MySQL
EXPLAIN
SELECT * FROM users WHERE id = 100;

-- Результат:
-- type: const ✓
-- key: PRIMARY
-- rows: 1

Важные замечания

  1. Индекс создаётся АВТОМАТИЧЕСКИ — не нужно создавать отдельный индекс для PRIMARY KEY

  2. Нельзя отключить индекс — первичный ключ всегда индексируется

  3. Один CLUSTERED индекс на таблицу (в SQL Server) — PRIMARY KEY обычно становится CLUSTERED индексом

  4. Индекс занимает место — на диске занимает дополнительное пространство (обычно 10-20% от размера таблицы)

  5. Замедляет вставку/удаление — СУБД должна обновлять индекс при каждой операции

Вывод

Да, первичный ключ ВСЕГДА автоматически индексируется во всех основных СУБД. Это один из ключевых свойств первичного ключа и выполняется без явного указания разработчика. Индекс гарантирует:

  • Быстрый поиск по PRIMARY KEY (O(log n))
  • Уникальность значений
  • Оптимизацию JOIN операций

Поэтому выбор первичного ключа критичен для производительности — лучше использовать числовые типы (INT, BIGINT) вместо строк, так как они быстрее индексируются.

Является ли первичный ключ проиндексированным в SQL? | PrepBro