Introspect postgresql 8.3 to find foreign keys
I'm trying to introspect a postgres 8.3 database to retrieve details of its foreign keys. Imagine I have the following schema:
CREATE TABLE "a" (
"id" SERIAL PRIMARY KEY
);
CREATE TABLE "b" (
"one" integer,
"two" integer,
"a_id" integer REFERENCES "a",
PRIMARY KEY ("one", "two")
);
CREATE TABLE "c" (
"id" SERIAL PRIMARY KEY,
"a_id" integer REFERENCES "a",
"b_one" integer,
"b_two" integer,
FOREIGN KEY ("b_one", "b_two") REFERENCES "b"
);
Then I'd like to run a query which producued the following:
table | columns | foreign table | foreign columns
--------------------------------------------------------
b | {a_id} | a | {id}
c | {a_id} | a | {id}
c | {b_one, b_two} | b | {one, two}
My first efforts have given me the query
SELECT conrelid开发者_如何学C::regclass as "table",
conkey as columns,
confrelid::regclass as "foreign table",
confkey as "foreign columns"
FROM pg_constraint
WHERE contype = 'f' ;
table | columns | foreign table | foreign columns
-------+---------+---------------+-----------------
b | {3} | a | {1}
c | {2} | a | {1}
c | {3,4} | b | {1,2}
which is almost there. But my efforts to convert the column numbers to column names have not yet provided me with the desired outcome. Googling has given me the below which is again, not quite right.
SELECT conrelid::regclass as "table",
a.attname as columns,
confrelid::regclass as "foreign table",
af.attname as "foreign columns"
FROM pg_attribute AS af,
pg_attribute AS a,
( SELECT conrelid,
confrelid,
conkey[i] AS conkey,
confkey[i] as confkey
FROM ( SELECT conrelid,
confrelid,
conkey,
confkey,
generate_series(1, array_upper(conkey, 1)) AS i
FROM pg_constraint
WHERE contype = 'f'
) AS ss
) AS ss2
WHERE af.attnum = confkey
AND af.attrelid = confrelid
AND a.attnum = conkey
AND a.attrelid = conrelid ;
table | columns | foreign table | foreign columns
-------+---------+---------------+-----------------
b | a_id | a | id
c | a_id | a | id
c | b_one | b | one
c | b_two | b | two
Can anyone help me take that final step?
Fleshing out Peter Eisentraut's answer; for postgresql 8.3 the array_agg function can be defined as
CREATE AGGREGATE array_accum (anyelement)
(
sfunc = array_append,
stype = anyarray,
initcond = '{}'
);
and then the full query to get my desired answer becomes
SELECT "table",
array_accum(columns) AS columns,
"foreign table",
array_accum("foreign columns") AS "foreign columns"
FROM ( SELECT conrelid::regclass AS "table",
a.attname as columns,
confrelid::regclass as "foreign table",
af.attname as "foreign columns"
FROM pg_attribute AS af,
pg_attribute AS a,
( SELECT conrelid,
confrelid,
conkey[i] AS conkey,
confkey[i] as confkey
FROM ( SELECT conrelid,
confrelid,
conkey,
confkey,
generate_series(1, array_upper(conkey, 1)) AS i
FROM pg_constraint
WHERE contype = 'f'
) AS ss
) AS ss2
WHERE af.attnum = confkey
AND af.attrelid = confrelid
AND a.attnum = conkey
AND a.attrelid = conrelid
) AS ss3
GROUP BY "table",
"foreign table";
Forgive the non-standard way of commenting on his answer, I'm still learning how to use Stackoverflow, and not having created an account in the first instance hasn't helped things.
SELECT table, array_agg(columns), foreign_table, array_agg(foreign_columns) FROM (your query here) GROUP BY table, foreign_table;
array_agg requires PostgreSQL 8.4. For earlier versions you can define your own (look for array_accum in the documentation). Obviously, you can merge this query into your big query, but this should give you the general idea.
精彩评论