PostgreSQL mencipta baris katalog kekunci asing tambahan kerana rujukan kepada jadual berpartisyen juga mesti berfungsi merentasi partisyen fizikalnya. Untuk menentukan sama ada baris tersebut mewakili satu hubungan atau pengisytiharan berasingan, periksa pautan induk dan jadual yang dirujuk.
Panduan ini ditujukan kepada pembangun yang menggunakan Mac untuk menyiasat senarai kekangan yang tidak dijangka dalam pelayar pangkalan data atau aplikasi Node.js. Tugasnya ialah menghasilkan semula tingkah laku tersebut dengan baris artikel sintetik, menjejaki keluarga kekangan dan menentukan perkara yang patut dimasukkan dalam laporan skema logik.
Mengapa satu kekunci asing menghasilkan beberapa baris kekangan?
Jadual berpartisyen menyediakan jadual logik yang barisnya disimpan dalam partisyennya. Jadual induk tidak mempunyai storan tersendiri. Perbezaan ini penting apabila jadual lain merujuk kepadanya: PostgreSQL mesti mengekalkan hubungan itu apabila baris dalam partisyen fizikal berubah. Lihat dokumentasi rasmi Pembahagian Jadual.
Pelaksanaan PostgreSQL 18 menjelaskan mekanisme ini. Rutinnya addFkRecurseReferenced mencipta pencetus tindakan kekunci asing dan secara rekursif mencipta baris pg_constraint tambahan bagi setiap partisyen yang dirujuk. Kod pengesahannya menjelaskan bahawa baris pada sisi yang dirujuk ini menyokong pencetus tindakan, bukannya imbasan pengesahan berasingan. Lihat pelaksanaan penciptaan dan pengesahan kekunci asing PostgreSQL.
Oleh itu, baris ini tidak bermaksud bahawa setiap nilai rujukan mesti wujud dalam setiap partisyen. Hubungan yang diisytiharkan masih menyasarkan induk berpartisyen. PostgreSQL juga perlu mengendalikan perubahan pada sisi yang dirujuk, termasuk tingkah laku yang dikonfigurasikan apabila baris yang dirujuk dipadamkan atau kuncinya berubah. Tingkah laku tersebut diterangkan dalam Kekunci Asing.
Versi PostgreSQL yang manakah menyokong ciri ini?
Kekunci asing yang merujuk kepada jadual berpartisyen diperkenalkan dalam PostgreSQL 12. Versi terdahulu tidak boleh menggunakan pengisytiharan yang menyasarkan induk yang sama. PostgreSQL 12 kini disenaraikan sebagai tidak disokong; versi itu menandakan sempadan sejarah ciri ini, bukannya sasaran pemasangan yang disyorkan. Artikel ini menggunakan dokumentasi PostgreSQL 18 semasa dan cabang sumbernya yang diselenggara. Lihat nota keluaran PostgreSQL 12.
Hasilkan semula hubungan dengan baris artikel sintetik
Gunakan pangkalan data pembangunan PostgreSQL tempatan yang sedia ada dan pemasangan psql yang berfungsi. Pada Mac anda, buka Terminal, sambung dengan psql -d postgres, kemudian semak pelayan dengan SHOW server_version;. Gantikan nama pangkalan data pembangunan anda jika perlu. Dokumentasi rasmi psql menerangkan pemilihan pangkalan data dan pelaksanaan SQL secara interaktif.
Jalankan persediaan lengkap ini sekali. Ia mencipta skema khusus tanpa membuang objek sedia ada. Jika skema itu sudah wujud, berhenti dan gunakan pangkalan data pembangunan baharu atau namakan semula skema itu secara konsisten dalam semua contoh.
BEGIN;
CREATE SCHEMA fk_partition_demo;
CREATE TABLE fk_partition_demo.articles (
article_id integer NOT NULL,
publication_year integer NOT NULL,
title text NOT NULL,
PRIMARY KEY (article_id, publication_year)
) PARTITION BY RANGE (publication_year);
CREATE TABLE fk_partition_demo.articles_2025
PARTITION OF fk_partition_demo.articles
FOR VALUES FROM (2025) TO (2026);
CREATE TABLE fk_partition_demo.articles_2026
PARTITION OF fk_partition_demo.articles
FOR VALUES FROM (2026) TO (2027);
CREATE TABLE fk_partition_demo.article_notes (
note_id integer PRIMARY KEY,
article_id integer NOT NULL,
publication_year integer NOT NULL,
note_text text NOT NULL,
CONSTRAINT article_notes_article_fk
FOREIGN KEY (article_id, publication_year)
REFERENCES fk_partition_demo.articles
(article_id, publication_year)
);
INSERT INTO fk_partition_demo.articles
(article_id, publication_year, title)
VALUES
(101, 2025, 'A guide to paper models'),
(102, 2026, 'Choosing a notebook layout');
INSERT INTO fk_partition_demo.article_notes
(note_id, article_id, publication_year, note_text)
VALUES
(1, 101, 2025, 'Add a materials list'),
(2, 102, 2026, 'Include a grid example');
COMMIT;
Kunci utama komposit ini disengajakan. PostgreSQL menghendaki kekangan kunci utama atau unik pada jadual berpartisyen merangkumi setiap lajur kunci partisyen, dengan sekatan tambahan terhadap ungkapan dalam kunci partisyen. Oleh sebab jadual ini dipartisikan mengikut publication_year, kunci utama merangkumi lajur itu dan kekunci asing merujuk kepada pasangan lengkap tersebut. Lihat Pembahagian Deklaratif: Batasan.
Oleh itu, sampel ini mengenal pasti artikel berdasarkan (article_id, publication_year). Ia tidak menetapkan bahawa article_id sahaja unik secara global. Jika aplikasi anda memerlukan rujukan yang hanya menggunakan article_id, semak semula reka bentuk pembahagian dan kunci sebelum menggunakan contoh ini.
Jejaki keluarga kekangan, bukan bandingkan nama
Medan katalog yang berguna ialah conrelid, iaitu jadual yang membuat rujukan; confrelid, iaitu jadual yang dirujuk; dan conparentid, iaitu OID kekangan induk. Nama kekangan tidak semestinya unik, jadi nama sahaja tidak dapat menentukan sama ada dua baris itu bebas antara satu sama lain. Medan ini didokumenkan dalam pg_constraint.
Selepas menjalankan persediaan, laksanakan pertanyaan ini. Pertanyaan ini bermula dengan kekunci asing akar yang diisytiharkan pada article_notes dan menjejaki kekangan anak secara rekursif.
WITH RECURSIVE fk_family AS (
SELECT c.oid, c.conparentid, c.oid AS root_oid, 0 AS depth
FROM pg_catalog.pg_constraint AS c
WHERE c.contype = 'f'
AND c.conrelid =
'fk_partition_demo.article_notes'::regclass
AND c.conparentid = 0
UNION ALL
SELECT child.oid, child.conparentid,
parent.root_oid, parent.depth + 1
FROM pg_catalog.pg_constraint AS child
JOIN fk_family AS parent
ON child.conparentid = parent.oid
WHERE child.contype = 'f'
)
SELECT
family.root_oid,
family.depth,
c.oid AS constraint_oid,
c.conname,
c.conparentid,
c.conrelid::regclass AS referencing_table,
c.confrelid::regclass AS referenced_table,
pg_get_constraintdef(c.oid) AS definition
FROM fk_family AS family
JOIN pg_catalog.pg_constraint AS c ON c.oid = family.oid
ORDER BY family.root_oid, family.depth, c.oid;
Bagi contoh satu aras ini, pelaksanaan tersebut membayangkan tiga baris kekunci asing: satu menyasarkan articles dan satu lagi bagi setiap dua partisyennya. Ini ialah hasil yang dijangka berdasarkan kod sumber, bukannya output pelaksanaan yang dirakam. Nama yang dijana dan nilai OID tidak patut dikodkan secara tetap. Sumber yang sama juga mencipta kekangan pada sisi yang dirujuk apabila partisyen dibuat atau dilampirkan kemudian. Lihat tablecmds.c.
| Baris dalam contoh ini | Jadual yang membuat rujukan | Jadual yang dirujuk | Pautan induk |
|---|---|---|---|
| Hubungan akar | article_notes |
articles |
conparentid = 0 |
| Anak partisyen yang dirujuk | article_notes |
articles_2025 |
OID kekangan akar |
| Anak partisyen yang dirujuk | article_notes |
articles_2026 |
OID kekangan akar |
Butiran yang mudah terlepas pandang ialah jadual yang membuat rujukan kekal sebagai article_notes dalam ketiga-tiga baris. Periksa kedua-dua lajur jadual dan pautan induk; melihat jadual yang dikaitkan dengan kekangan sahaja boleh menyembunyikan struktur partisyen yang dirujuk.
Pertanyaan ini juga memaparkan takrif yang dibina semula melalui pg_get_constraintdef(). PostgreSQL mendokumenkan fungsi ini sebagai pembinaan semula, bukannya SQL asal yang dihantar. Gunakannya untuk memahami hubungan, bukan untuk mendapatkan semula teks migrasi yang tepat. Lihat Fungsi dan Operator Maklumat Sistem.
Periksa baris yang sama dengan node-postgres pada Mac anda
node-postgres memulangkan hasil pertanyaan pelayan; baris berkaitan partisyen berasal daripada mekanisme kekangan PostgreSQL. API pertanyaannya yang didokumenkan menerima teks SQL dan tatasusunan nilai parameter yang berasingan. Di sini, nama hubungan dihantar sebagai nilai teks yang ditukar oleh PostgreSQL kepada regclass. Lihat Pertanyaan.
Dengan versi Node.js terkini yang disokong telah dipasang, cipta direktori kerja dan pasang pg. Arahan pemasangan dan panduan keserasian masa jalan terdapat dalam pengenalan node-postgres.
mkdir pg-fk-inspection
cd pg-fk-inspection
npm init -y
npm install pg
Simpan atur cara lengkap berikut sebagai inspect-fks.mjs. Atur cara ini memerlukan persediaan SQL di atas telah dilaksanakan. Tidak seperti laporan SQL rekursif, atur cara ini menyenaraikan baris yang dilampirkan terus pada jadual yang diberikan sebagai jadual pembuat rujukan; itu memadai untuk jadual article_notes yang tidak berpartisyen dalam contoh ini.
import pg from 'pg';
const { Client } = pg;
const client = new Client({ connectionTimeoutMillis: 5000 });
const relation = process.argv[2] ??
'fk_partition_demo.article_notes';
try {
await client.connect();
const version = await client.query('SHOW server_version');
console.log('PostgreSQL:', version.rows[0].server_version);
const result = await client.query(`
SELECT oid, conname,
conrelid::regclass::text AS referencing_table,
confrelid::regclass::text AS referenced_table,
conparentid,
conparentid = 0 AS is_root,
pg_get_constraintdef(oid) AS definition
FROM pg_catalog.pg_constraint
WHERE contype = 'f' AND conrelid = $1::regclass
ORDER BY conparentid, oid
`, [relation]);
console.table(result.rows);
} catch (error) {
console.error(error.message);
process.exitCode = 1;
} finally {
await client.end();
}
Jalankannya pada pangkalan data yang sama seperti yang digunakan untuk persediaan SQL. node-postgres menggunakan pemboleh ubah persekitaran sambungan PostgreSQL, termasuk PGDATABASE, dan mendokumenkan nilai lalainya dalam Menyambung.
PGDATABASE=postgres node inspect-fks.mjs
Jika hubungan tidak dapat ditemui, pastikan kedua-dua sesi menggunakan pangkalan data yang sama dan persediaan itu berjaya dilaksanakan. Hasil kosong bermaksud pertanyaan ini tidak menemui kekunci asing yang dilampirkan terus pada hubungan itu; pertanyaan ini tidak melaporkan rujukan masuk daripada jadual lain. JavaScript telah lulus semakan sintaks semasa penyediaan artikel ini. Pelaksanaan pangkalan data tidak tersedia, jadi contoh SQL dan struktur katalog yang dijangka disemak berdasarkan dokumentasi dan kod sumber yang diperoleh.
Tentukan perkara yang patut dipaparkan atau disiasat
Gunakan turutan berikut apabila senarai kekangan kelihatan lebih panjang daripada yang dijangka. Turutan ini membezakan laporan hubungan logik daripada inventori pelaksanaan tanpa mengetepikan bukti yang berguna.
- Sahkan hubungan dan versi pelayan. Gunakan nama hubungan yang layak dengan skema dan rekodkan
SHOW server_version;sebelum membandingkan tingkah laku dengan dokumentasi. - Ikuti pautan induk. Bagi contoh ini, baris anak yang berkongsi OID akar tergolong dalam keluarga kekangan hubungan yang diisytiharkan.
- Bandingkan kedua-dua hujung. Nilai
confrelidyang berubah dengan jadual pembuat rujukan yang sama menerangkan baris partisyen yang dirujuk di atas. - Pilih tujuan laporan. Untuk hubungan akar yang diisytiharkan pada jadual yang dipilih, tapis dengan
contype = 'f' AND conparentid = 0. Untuk keluarga lengkap, kekalkan laporan rekursif. - Siasat akar berasingan secara individu. Bandingkan pasangan lajur, jadual yang dirujuk, tindakan, keadaan pengesahan dan kebolehtangguhan sebelum memutuskan bahawa dua pengisytiharan itu berlebihan.
Pendekatan pelaporan ini mengikuti pengecam dan medan hubungan yang didokumenkan dalam pg_constraint. Penapis akar sahaja ini mempunyai skop yang disengajakan: partisyen mungkin terlibat melalui kekangan anak yang diwarisi, jadi menapis jadual daun kepada akar bukanlah laporan lengkap bagi setiap hubungan yang mempengaruhinya.
Jika anda memerlukan bukti tentang objek penguatkuasaan, pg_trigger.tgconstraint memautkan pencetus kepada kekangannya, manakala tgrelid mengenal pasti jadual yang membawa pencetus itu. Katalog ini juga mengenal pasti pencetus yang dijana secara dalaman. Medan ini menyediakan laluan pemeriksaan tambahan dalam pg_trigger.
Kekalkan baris yang dijana selagi hubungan yang diisytiharkan masih diperlukan. Bagi kes yang dihasilkan semula ini, penyelesaian praktikalnya ialah memaparkan hubungan akar dalam paparan skema logik dan mengekalkan anak-anaknya dalam paparan diagnostik. Setelah pautan induk dan kedua-dua titik hujung jadual kelihatan, baris tambahan itu menerangkan cara PostgreSQL melaksanakan rujukan merentasi partisyen.
Sumber
Pembahagian Jadual — Kumpulan Pembangunan Global PostgreSQL.
pg_constraint — Kumpulan Pembangunan Global PostgreSQL.
Keluaran 12 — Kumpulan Pembangunan Global PostgreSQL.
tablecmds.c, REL_18_STABLE — Kumpulan Pembangunan Global PostgreSQL.
Kekangan — Kumpulan Pembangunan Global PostgreSQL.
psql — Kumpulan Pembangunan Global PostgreSQL.
Fungsi dan Operator Maklumat Sistem — Kumpulan Pembangunan Global PostgreSQL.
pg_trigger — Kumpulan Pembangunan Global PostgreSQL.
Pengenalan node-postgres — node-postgres.
Pertanyaan — node-postgres.
Menyambung — node-postgres.