بنقرة واحدة
postgresql-review
PostgreSQL query review, optimalisering og beste praksis for Nav-applikasjoner
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
PostgreSQL query review, optimalisering og beste praksis for Nav-applikasjoner
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Generer conventional commit-meldinger med Nav-relevante scopes og breaking change-format
Expert builder for the Aksel design system (Nav / @navikt) React components, design tokens, layout primitives, theming (light/dark), icons, CSS, the Tailwind preset, version migrations, and Figma-to-code. Trigger on any frontend UI task that mentions Aksel, Nav/Navikt, "designsystemet", or @navikt/ds-* / @navikt/aksel-* packages — or that asks to add, create, build, or refactor a component (button, input, modal, table, alert, card, form) or layout, or to implement a design from Figma (a pasted figma.com/design/...?node-id link, "implement this design", "build this from Figma", design-to-code). Strong signals "using/with aksel", "@navikt/ds-react", "design system", a pasted figma.com link. If the work is frontend UI and there is any Aksel signal, invoke this skill unless the user explicitly opts out.
Integrer og konfigurer Nav Dekoratøren – felles header og footer for nav.no-applikasjoner. Bruk når et team skal ta i bruk Dekoratøren, oppdatere konfigurasjon, legge til breadcrumbs/språkvelger/analytics, håndtere samtykke (ekomloven), CSP eller feilsøke integrasjon mot dekoratøren.
Lag responsive layouts med Aksel Design System (v8+) - spacing tokens, layout primitives (Box, HStack, VStack, HGrid, Page, Bleed) og ResponsiveProp
Generer og kjør Playwright E2E-tester for webapplikasjoner med page objects, auth fixtures og tilgjengelighetstester
Kompakt output-stil som kutter fyllord og beholder teknisk substans — spar output-tokens uten å miste nøyaktighet.
| name | postgresql-review |
| description | PostgreSQL query review, optimalisering og beste praksis for Nav-applikasjoner |
| license | MIT |
| compatibility | PostgreSQL database |
| metadata | {"domain":"backend","tags":"postgresql sql optimization review indexing"} |
Review and optimize PostgreSQL queries, schemas, and patterns for Nav applications. Covers EXPLAIN analysis, index strategies, JSONB patterns, and common anti-patterns.
Run EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) to analyze queries:
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM vedtak
WHERE bruker_id = '12345678901'
AND status = 'aktiv'
ORDER BY opprettet_dato DESC
LIMIT 10;
| Sign | Problem | Solution |
|---|---|---|
Seq Scan on large table | Missing index | CREATE INDEX |
Sort with external merge | Not enough work_mem | Increase work_mem or add index with correct sort order |
Nested Loop with high rows | Cartesian product / missing join index | Add index on join column |
Hash Join with Batches > 1 | work_mem too low | Increase work_mem for the session |
Large difference between estimated and actual rows | Outdated statistics | ANALYZE tablename; |
-- Simple index for lookups
CREATE INDEX idx_vedtak_bruker_id ON vedtak(bruker_id);
-- Composite index — columns in order of selectivity
CREATE INDEX idx_vedtak_bruker_status ON vedtak(bruker_id, status);
-- Partial index — only relevant rows
CREATE INDEX idx_vedtak_aktive ON vedtak(bruker_id)
WHERE status = 'aktiv';
-- Covering index — avoids table lookup
CREATE INDEX idx_vedtak_covering ON vedtak(bruker_id, status)
INCLUDE (opprettet_dato, belop);
-- Concurrent — no table locking (requires outside transaction)
CREATE INDEX CONCURRENTLY idx_vedtak_dato ON vedtak(opprettet_dato);
| Scenario | Index Type |
|---|---|
WHERE a = x | B-tree on a |
WHERE a = x AND b = y | Composite (a, b) |
WHERE a = x AND status = 'aktiv' | Partial index WHERE status = 'aktiv' |
WHERE a LIKE 'prefix%' | B-tree (prefix only) |
WHERE a @> '{"key": "val"}' | GIN on JSONB |
| Full-text search | GIN with to_tsvector |
| Geography | GiST |
-- ✅ Correct — GIN index for JSONB queries
CREATE INDEX idx_metadata_gin ON hendelser USING GIN (metadata);
-- Query JSONB
SELECT * FROM hendelser
WHERE metadata @> '{"type": "vedtak", "tema": "dagpenger"}';
-- Fetch nested values
SELECT
id,
metadata->>'type' AS type,
metadata->'detaljer'->>'belop' AS belop
FROM hendelser;
-- ❌ Wrong — casting in WHERE without index
SELECT * FROM hendelser
WHERE (metadata->>'opprettet')::timestamp > NOW() - INTERVAL '7 days';
-- ✅ Better — use expression index
CREATE INDEX idx_metadata_opprettet ON hendelser (((metadata->>'opprettet')::timestamp));
-- ✅ Correct — CTE for readability
WITH aktive_vedtak AS (
SELECT bruker_id, COUNT(*) AS antall
FROM vedtak
WHERE status = 'aktiv'
GROUP BY bruker_id
),
siste_aktivitet AS (
SELECT bruker_id, MAX(opprettet_dato) AS sist_aktiv
FROM aktivitetslogg
GROUP BY bruker_id
)
SELECT
av.bruker_id,
av.antall,
sa.sist_aktiv
FROM aktive_vedtak av
JOIN siste_aktivitet sa USING (bruker_id)
WHERE av.antall > 1;
-- Ranking within group
SELECT
bruker_id,
vedtak_id,
opprettet_dato,
ROW_NUMBER() OVER (PARTITION BY bruker_id ORDER BY opprettet_dato DESC) AS rn
FROM vedtak
WHERE rn = 1; -- Latest vedtak per user
-- Running total
SELECT
dato,
antall,
SUM(antall) OVER (ORDER BY dato) AS kumulativt
FROM daglig_statistikk;
// ❌ Wrong — N+1: one query per user
val brukere = repository.findAll()
brukere.forEach { bruker ->
val vedtak = vedtakRepository.findByBrukerId(bruker.id) // N extra queries
}
// ✅ Correct — JOIN or batch query
val brukereOgVedtak = repository.findAllWithVedtak() // Single query with JOIN
-- ❌ Wrong — fetches all columns incl. large JSONB/TEXT
SELECT * FROM dokument WHERE bruker_id = '12345';
-- ✅ Correct — only necessary columns
SELECT id, tittel, opprettet_dato FROM dokument WHERE bruker_id = '12345';
-- ❌ Wrong — can return millions of rows
SELECT * FROM hendelse WHERE type = 'innlogging';
-- ✅ Correct — always limit result set
SELECT * FROM hendelse WHERE type = 'innlogging'
ORDER BY opprettet_dato DESC
LIMIT 100;
// HikariCP — recommended configuration for Nais
HikariDataSource().apply {
jdbcUrl = System.getenv("DB_JDBC_URL")
?: "jdbc:postgresql://${System.getenv("DB_HOST")}:5432/${System.getenv("DB_DATABASE")}"
username = System.getenv("DB_USERNAME")
password = System.getenv("DB_PASSWORD")
maximumPoolSize = 5 // Nais: start low, scale up as needed
minimumIdle = 1
connectionTimeout = 10_000
idleTimeout = 300_000
maxLifetime = 600_000
validationTimeout = 5_000
}
-- Add column with default (PostgreSQL 11+ — instant, no rewrite)
ALTER TABLE stor_tabell ADD COLUMN ny_kolonne BOOLEAN DEFAULT false;
-- Create index without locking the table
CREATE INDEX CONCURRENTLY idx_ny ON stor_tabell(ny_kolonne);
-- Batch update (avoid long transaction)
-- Run in application code with batches of 10,000 rows:
UPDATE stor_tabell SET ny_kolonne = true WHERE id BETWEEN $1 AND $2;
WHERE columns have indexes?EXPLAIN ANALYZE been run for new/changed queries?SELECT * in production code?LIMIT on queries that can return many rows?CONCURRENTLY?