Hier ist eine andere Lösung für die alte Frage. Im Laufe der Jahre gab es viele ausgezeichnete Antworten auf diese Frage, und mein Versuch lehnt sich stark an sie an.
Ich habe die Lösung von Andrey Lebedenko als Ausgangspunkt verwendet, da ihre Ergebnisse meinen Anforderungen bereits sehr nahe kamen.
Merkmale:
- Nach gängiger Praxis habe ich die Fremdschlüsselbegrenzungen außerhalb der Tabellendefinition verschoben. Sie sind jetzt als ALTER TABLE-Anweisungen am Ende der Tabelle enthalten. Der Grund dafür ist, dass ein Fremdschlüssel auch auf eine Spalte derselben Tabelle verweisen kann. In diesem Grenzfall kann die Einschränkung erst erstellt werden, nachdem die Tabellenerstellung abgeschlossen ist. Die Anweisung create table würde sonst einen Fehler auslösen.
- Das Layout und die Einrückung sehen jetzt besser aus (zumindest für mein Auge)
- Befehl Drop (auskommentiert) in der Kopfzeile der Definition
- Die Lösung wird hier in Form einer plpgsql-Funktion angeboten. Der Algorithmus verwendet jedoch keine prozedurale Sprache. Die Funktion umhüllt lediglich eine einzige Abfrage, die auch in einem reinen SQL-Kontext verwendet werden kann.
- Redundante Unterabfragen entfernt
- Bezeichner werden jetzt in Anführungszeichen gesetzt, wenn sie mit reservierten Postgresql-Sprachelementen identisch sind
- den String-Verkettungsoperator || durch die entsprechenden String-Funktionen ersetzt, um Leistung, Sicherheit und Lesbarkeit des Codes zu verbessern. Hinweis: Der Operator || erzeugt NULL, wenn eine der kombinierten Zeichenketten NULL ist. Er sollte nur verwendet werden, wenn dies das gewünschte Verhalten ist. (Sehen Sie sich die Verwendung im untenstehenden Code als Beispiel)
CREATE OR REPLACE FUNCTION public.wmv_get_table_definition (
p_schema_name character varying,
p_table_name character varying
)
RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN query
WITH table_rec AS (
SELECT
c.relname, n.nspname, c.oid
FROM
pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE
relkind = 'r'
AND n.nspname = p_schema_name
AND c.relname LIKE p_table_name
ORDER BY
c.relname
),
col_rec AS (
SELECT
a.attname AS colname,
pg_catalog.format_type(a.atttypid, a.atttypmod) AS coltype,
a.attrelid AS oid,
' DEFAULT ' || (
SELECT
pg_catalog.pg_get_expr(d.adbin, d.adrelid)
FROM
pg_catalog.pg_attrdef d
WHERE
d.adrelid = a.attrelid
AND d.adnum = a.attnum
AND a.atthasdef) AS column_default_value,
CASE WHEN a.attnotnull = TRUE THEN
'NOT NULL'
ELSE
'NULL'
END AS column_not_null,
a.attnum AS attnum
FROM
pg_catalog.pg_attribute a
WHERE
a.attnum > 0
AND NOT a.attisdropped
ORDER BY
a.attnum
),
con_rec AS (
SELECT
conrelid::regclass::text AS relname,
n.nspname,
conname,
pg_get_constraintdef(c.oid) AS condef,
contype,
conrelid AS oid
FROM
pg_constraint c
JOIN pg_namespace n ON n.oid = c.connamespace
),
glue AS (
SELECT
format( E'-- %1$I.%2$I definition\n\n-- Drop table\n\n-- DROP TABLE IF EXISTS %1$I.%2$I\n\nCREATE TABLE %1$I.%2$I (\n', table_rec.nspname, table_rec.relname) AS top,
format( E'\n);\n\n\n-- adempiere.wmv_ghgaudit foreign keys\n\n', table_rec.nspname, table_rec.relname) AS bottom,
oid
FROM
table_rec
),
cols AS (
SELECT
string_agg(format(' %I %s%s %s', colname, coltype, column_default_value, column_not_null), E',\n') AS lines,
oid
FROM
col_rec
GROUP BY
oid
),
constrnt AS (
SELECT
string_agg(format(' CONSTRAINT %s %s', con_rec.conname, con_rec.condef), E',\n') AS lines,
oid
FROM
con_rec
WHERE
contype <> 'f'
GROUP BY
oid
),
frnkey AS (
SELECT
string_agg(format('ALTER TABLE %I.%I ADD CONSTRAINT %s %s', nspname, relname, conname, condef), E';\n') AS lines,
oid
FROM
con_rec
WHERE
contype = 'f'
GROUP BY
oid
)
SELECT
concat(glue.top, cols.lines, E',\n', constrnt.lines, glue.bottom, frnkey.lines, ';')
FROM
glue
JOIN cols ON cols.oid = glue.oid
LEFT JOIN constrnt ON constrnt.oid = glue.oid
LEFT JOIN frnkey ON frnkey.oid = glue.oid;
END;
$BODY$
LANGUAGE plpgsql;