Wzorce optymalizacji SQL
Zapytanie do dashboardu, które na Twoim laptopie wykonywało się w 40 ms, w produkcji przekracza limit czasu po 12 sekundach. Tabela urosła z 50 tys. wierszy do 80 mln, optymalizator po cichu przełączył się na skanowanie sekwencyjne, a Twój APM świeci się na czerwono. Wklejasz zapytanie do asystenta AI i dostajesz pewną siebie odpowiedź „dodaj indeks na customer_id” — tyle że ten indeks już istnieje, a planner i tak go ignoruje.
Rozwiązaniem jest przestać zgadywać. Kiedy podłączysz serwer MCP bazy danych do Cursora, Claude Code lub Codeksa, asystent czyta Twój rzeczywisty plan wykonania, Twoje rzeczywiste statystyki tabel i Twoje rzeczywiste użycie indeksów — a potem proponuje zmiany, które możesz zweryfikować na prawdziwych liczbach zamiast na ludowych wierzeniach.
Co z tego wyniesiesz
Dział zatytułowany „Co z tego wyniesiesz”- Podłączony serwer MCP Postgresa, dzięki któremu asystent czyta żywy schemat i wynik
EXPLAINzamiast halucynować - Gotowy prompt, który zamienia zrzut
EXPLAIN (ANALYZE, BUFFERS)w uporządkowaną listę wąskich gardeł - Prompt rekomendujący indeksy na podstawie rzeczywistych wzorców zapytań — i odrzucający te zbędne
- Prompt przepisujący podzapytanie na funkcję okna z zachowaniem wyników
- Jasne wyczucie, kiedy rady AI wprowadzą Cię w błąd (małe tabele, zmaterializowane CTE, efekty uboczne
EXPLAIN ANALYZE)
Najpierw podłącz serwer MCP bazy danych
Dział zatytułowany „Najpierw podłącz serwer MCP bazy danych”Bez serwera MCP asystent rozumuje o schemacie, którego nie widzi. Z nim może uruchomić EXPLAIN, zajrzeć do pg_stat_user_indexes i sprawdzić statystyki kolumn bezpośrednio. Konfiguracja jest identyczna w Cursorze, Claude Code i Codeksie — wszystkie czytają tę samą konfigurację MCP; różni się tylko lokalizacja pliku.
Do pracy lokalnej i operacji świadomych schematu Prisma Postgres MCP jest dostarczany w ramach Prisma CLI i nie wymaga dodatkowej instalacji:
{ "mcpServers": { "postgres": { "command": "npx", "args": ["-y", "prisma", "mcp"] } }}Wrzuć konfigurację do .cursor/mcp.json (Cursor), zarejestruj ją przez claude mcp add (Claude Code) lub dodaj do ~/.codex/config.toml w sekcji [mcp_servers.postgres] (Codex). Potem opisany niżej przepływ pracy jest wszędzie taki sam.
Czytaj plan wykonania jak doświadczony DBA
Dział zatytułowany „Czytaj plan wykonania jak doświadczony DBA”Pojedynczym ruchem o największej dźwigni jest podanie asystentowi prawdziwego planu EXPLAIN (ANALYZE, BUFFERS) i poproszenie go o uszeregowanie problemów według kosztu. Ogólne prompty „zoptymalizuj to zapytanie” dają ogólne porady; prompt oparty na planie daje konkrety.
To właśnie więzy oparte na danych — „sprawdź pg_stats”, „nie sugeruj istniejących indeksów”, „oznacz Rows Removed by Filter” — odróżniają tę odpowiedź od tego, co zwróciłaby wyszukiwarka. Rozjazd szacunku liczby wierszy o 10x niemal zawsze oznacza nieaktualne statystyki (ANALYZE tabeli), a nie brakujący indeks — i asystent wychwyci to tylko wtedy, gdy każesz mu spojrzeć.
Rekomenduj indeksy na podstawie rzeczywistego użycia
Dział zatytułowany „Rekomenduj indeksy na podstawie rzeczywistego użycia”Gdy plan wskaże na skanowanie, kolejne pytanie brzmi: który indeks — i czy już nie masz takiego, który wykonuje to zadanie. Tu czytanie pg_stat_user_indexes bije intuicję: zespoły rutynowo dźwigają po kilkanaście nieużywanych indeksów, które spowalniają każdy zapis.
W trybie Agent, z podłączonym Postgres MCP, dołącz schemat i folder migracji jako kontekst i pozwól agentowi zarówno zdiagnozować, jak i napisać migrację:
@migrations Using the Postgres MCP, query pg_stat_user_indexes andpg_stat_user_tables for the orders and order_items tables. Then:
- List indexes with idx_scan = 0 over the last stats window (candidates to drop)- Propose the minimal set of indexes for the WHERE/JOIN/ORDER BY in the slow dashboard query, preferring one composite index over several single-column ones where the leading column is selective- Write the CREATE INDEX CONCURRENTLY statements as a new migration file- Estimate the write-amplification cost of each new indexWskaż Claude Code katalog migracji (katalog, nie plik) i steruj nim z terminala:
claude --add-dir db/migrationsNastępnie w sesji:
Using the Postgres MCP server, analyze index usage on orders andorder_items via pg_stat_user_indexes. Recommend indexes for the slowdashboard query, reject any that duplicate an existing index, and writethem as a timestamped migration using CREATE INDEX CONCURRENTLY so wedon't lock the table in production.Codex czyta tę samą konfigurację MCP z ~/.codex/config.toml. Uruchom go bezgłowo, by wygenerować migrację jako gotowy do przejrzenia diff:
codex exec --ask-for-approval on-request \ "Using the Postgres MCP server, query pg_stat_user_indexes for orders and order_items, propose the minimal index set for the slow dashboard query, and write CREATE INDEX CONCURRENTLY statements to a new file in db/migrations. Reject indexes that duplicate existing ones."codex exec utrzymuje przebieg w trybie nieinteraktywnym i pozostawia zmianę jako diff, który przeglądasz przed zastosowaniem — idealne do wpięcia w worktree lub check CI.
Wzorce przepisywania, z których optymalizator Cię nie wybawi
Dział zatytułowany „Wzorce przepisywania, z których optymalizator Cię nie wybawi”Część wolnych zapytań to nie problem indeksowania — to problem kształtu. Skorelowane podzapytania, które uruchamiają się ponownie dla każdego wiersza, to klasyczny przypadek, a lekarstwem jest funkcja okna. Zawsze proś asystenta, by udowodnił, że przepisana wersja zwraca identyczne wiersze.
Asystent powinien zwrócić oczywistą postać SUM(...) OVER (PARTITION BY customer_id ORDER BY order_date) — ale to weryfikacja EXCEPT w obie strony jest tym, co naprawdę się liczy. Domyślne ramki okna różnią się od naiwnego podzapytania (RANGE kontra ROWS, obsługa remisów przy równym order_date), a po cichu zmienione wyniki to najdroższy rodzaj „optymalizacji”.
Konkretnie dla PostgreSQL 18 dopominaj się o właściwy typ indeksu pod dany wzorzec dostępu, zamiast wszędzie wstawiać B-tree:
-- Indeks częściowy dla gorącej ścieżki (tylko aktywne wiersze) — mniejszy, szybszyCREATE INDEX CONCURRENTLY idx_active_customers ON customers (customer_id) WHERE status = 'active';
-- GIN dla zawierania JSONB / wyszukiwania pełnotekstowegoCREATE INDEX CONCURRENTLY idx_product_attributes ON products USING gin (attributes);
-- BRIN dla danych naturalnie uporządkowanych w czasie, tylko-do-dopisywania — maleńki na dyskuCREATE INDEX CONCURRENTLY idx_events_created_at ON events USING brin (created_at);Strategiczna denormalizacja dla ścieżek ciężkich odczytowo
Dział zatytułowany „Strategiczna denormalizacja dla ścieżek ciężkich odczytowo”Gdy złączenie jest gorące, a leżące pod nim wiersze rzadko się zmieniają, utrzymywana tabela podsumowań bije ponowne agregowanie przy każdym żądaniu. Poproś asystenta o wygenerowanie zarówno tabeli, jak i wyzwalacza, który utrzymuje ją w poprawnym stanie — to o wyzwalaczu ludzie zapominają, a nieaktualne podsumowanie jest gorsze niż wolne zapytanie.
Liczenia przybliżone: poznaj swój silnik
Dział zatytułowany „Liczenia przybliżone: poznaj swój silnik”Subtelna pułapka dokładności: APPROX_COUNT_DISTINCT() nie jest natywną funkcją PostgreSQL ani MySQL. Istnieje w BigQuery, Snowflake i Sparku, więc sugestie AI, które ją zawierają, na czystym Postgresie rzucą błędem function approx_count_distinct does not exist.
- BigQuery / Snowflake:
APPROX_COUNT_DISTINCT(customer_id)jest natywne — używaj śmiało. - PostgreSQL: zainstaluj rozszerzenie
hlli używajhll_cardinality(hll_add_agg(hll_hash_bigint(customer_id)))albo pogódź się z dokładnymCOUNT(DISTINCT ...)wspartym indeksem pokrywającym. - MySQL: nie ma wbudowanego przybliżonego liczenia odrębnych wartości; użyj dokładnego
COUNT(DISTINCT ...)lub utrzymuj tabelę licznikową.
To dokładnie ten rodzaj błędu między silnikami, któremu zapobiega podłączony serwer MCP — asystent może potwierdzić, że funkcja istnieje, zanim ją zaproponuje.