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

Поддерживает ли HASH операции неравенства в PostgreSQL?

2.2 Middle🔥 161 комментариев
#Python Core

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

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

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

HASH индексы и операции неравенства в PostgreSQL

Нет, HASH индексы в PostgreSQL не поддерживают операции неравенства (!=, <>, <, >, <=, >=). Они работают только с оператором равенства (=).

Какие операции поддерживают HASH индексы

-- ✅ Работает — оператор равенства
SELECT * FROM users WHERE email = user@example.com;

-- ✅ Работает — IN с равенством
SELECT * FROM users WHERE email IN (user1@example.com, user2@example.com);

-- ❌ НЕ работает — неравенство
SELECT * FROM users WHERE email != admin@example.com;

-- ❌ НЕ работает — диапазон
SELECT * FROM users WHERE age > 18;

-- ❌ НЕ работает — LIKE
SELECT * FROM users WHERE email LIKE %@example.com;

Почему? Природа HASH индексов

HASH индекс создаёт хеш-таблицу для быстрого поиска:

Исходные значения: ["alice@example.com", "bob@example.com", "charlie@example.com"]
                              ↓
HASH функция: HASH(value) → целое число
                              ↓
Хеш-таблица: {2847: alice, 5923: bob, 1034: charlie}

Проблема: хеш-функция необратима и не сохраняет порядок:

# Пример хеша
hash("alice@example.com") = 2847
hash("bob@example.com") = 5923

# Для поиска alice нужен ровно этот хеш (2847)
# Но для поиска всех != alice нельзя просто исключить хеш (5923)
# Потому что нужно проверить все остальные значения

Типы индексов и их возможности

Тип индекса=!=< >LIKEINLIKE
HASH✅*
BTREE
GIN
GIST
BRIN

*IN работает как несколько = операций

Примеры в PostgreSQL

-- Создание HASH индекса
CREATE INDEX idx_email_hash ON users USING HASH (email);

-- Тест: этот запрос использует HASH индекс
EXPLAIN ANALYZE
SELECT * FROM users WHERE email = user@example.com;
-- Index Scan using idx_email_hash on users

-- Тест: этот запрос НЕ использует HASH индекс
EXPLAIN ANALYZE
SELECT * FROM users WHERE email != user@example.com;
-- Seq Scan on users (полный скан таблицы!)

Почему BTREE индекс лучше для большинства случаев

-- Создание BTREE индекса (по умолчанию)
CREATE INDEX idx_email_btree ON users USING BTREE (email);

-- Все эти операции используют индекс
SELECT * FROM users WHERE email = user@example.com;      -- ✅ Индекс
SELECT * FROM users WHERE email != user@example.com;     -- ✅ Индекс (сортировка)
SELECT * FROM users WHERE email LIKE user%;              -- ✅ Индекс (префикс)
SELECT * FROM users WHERE email > a AND email < z;    -- ✅ Индекс (диапазон)

Когда использовать HASH индекс

HASH индексы используют редко в современном PostgreSQL:

-- Раньше HASH был хорош для больших таблиц
-- Но с версии PostgreSQL 10+ BTREE стал быстрее и универсальнее

-- HASH всё ещё может быть полезен для:
-- 1. Очень больших таблиц (>100GB) с частыми точными поисками
-- 2. Когда BTREE занимает слишком много памяти

CREATE INDEX idx_huge_table_hash ON huge_table USING HASH (id);

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

-- Узнать, какой индекс используется
SELECT 
    indexname,
    indexdef
FROM pg_indexes
WHERE tablename = users
ORDER BY indexname;

-- Пример вывода:
-- indexname          | indexdef
-- idx_email_hash     | CREATE INDEX idx_email_hash ON users USING HASH (email)
-- idx_email_btree    | CREATE INDEX idx_email_btree ON users USING BTREE (email)

Практический пример в Python

from sqlalchemy import create_engine, Index
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = users
    
    id = Column(Integer, primary_key=True)
    email = Column(String(255))

# Создание HASH индекса (работает для поиска по email)
idx_hash = Index(idx_email_hash, User.email, postgresql_using=hash)

# Создание BTREE индекса (работает для всех операций)
idx_btree = Index(idx_email_btree, User.email, postgresql_using=btree)

engine = create_engine(postgresql://...)
Base.metadata.create_all(engine)

# Использование
with Session(engine) as session:
    # Использует индекс (= работает в обоих)
    user = session.query(User).filter(User.email == test@example.com).first()
    
    # Использует только BTREE индекс (HASH не поддерживает !=)
    users = session.query(User).filter(User.email != admin@example.com).all()

Откуда берётся информация в PostgreSQL документации

-- Проверить в документации PostgreSQL 15
-- https://www.postgresql.org/docs/current/indexes-types.html

-- Hash Index Documentation:
-- "Hash indexes can only handle simple equality comparisons."
-- "B-tree indexes handle equality and range searches on data that can be sorted into some ordering."

Когда запрос падает через HASH

from sqlalchemy import and_, or_

# ❌ Это замедлится (не использует HASH индекс)
users = session.query(User).filter(
    or_(
        User.email == user1@example.com,
        User.email != admin@example.com  # Неравенство
    )
).all()

# ✅ Лучше использовать BTREE индекс
idx_btree = Index(idx_email_btree, User.email, postgresql_using=btree)

# Теперь работает эффективно
users = session.query(User).filter(
    or_(
        User.email == user1@example.com,
        User.email != admin@example.com
    )
).all()

Рекомендация

Используй BTREE индекс по умолчанию — это универсальный вариант. HASH индекс имеет нишевое применение в высоконагруженных системах с точными поисками по очень большим таблицам.

-- Хорошая практика
CREATE INDEX idx_email ON users USING BTREE (email);

-- Избегай если не уверен
-- CREATE INDEX idx_email ON users USING HASH (email);

Вывод: HASH индексы не поддерживают неравенства — это архитектурное ограничение хеш-таблиц. Используй BTREE для полноценной поддержки всех операций.

Поддерживает ли HASH операции неравенства в PostgreSQL? | PrepBro