用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/navikt/copilot --skill postgresql-review命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Expert builder for Aksel, the Nav / @navikt design system — React components, design tokens, layout primitives, theming (light/dark), icons, CSS, the Tailwind preset, version migrations, Figma-to-code. Triggers — Aksel, "using/with aksel", Nav/Navikt, "designsystemet", "design system", @navikt/ds-* (e.g. @navikt/ds-react) or @navikt/aksel-* packages; add/create/build/refactor a component (button, input, modal, table, alert, card, form) or layout; implement a design from Figma (pasted figma.com/design/...?node-id link, "implement this design", "build this from Figma", design-to-code). Invoke for frontend UI work with any Aksel signal unless the user opts out.
Migrer Jackson 2.x til Jackson 3.x (tools.jackson) i Kotlin/Java-prosjekter — automatisert OpenRewrite-pass pluss manuell Kotlin-spesifikk opprydding og verifisering
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.
基于 SOC 职业分类
正在显示 SKILL.md
| 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?