How to generate the "create table" sql statement for an existing table in postgreSQL - postgresql

I have created a table in postgreSQL. I want to look at the SQL statement used to create the table but cannot figure it out.
How do I get the create table SQL statement for an existing table in Postgres via commandline or SQL statement?

pg_dump -t 'schema-name.table-name' --schema-only database-name
More info - in the manual.

(NOTICE - this solution is not working with PostgreSQL v12+)
My solution is to log in to the postgres db using psql with the -E option as follows:
psql -E -U username -d database
In psql, run the following commands to see the sql that postgres uses to generate
the describe table statement:
-- List all tables in the schema (my example schema name is public)
\dt public.*
-- Choose a table name from above
-- For create table of one public.tablename
\d+ public.tablename
Based on the sql echoed out after running these describe commands, I was able to put together
the following plpgsql function:
CREATE OR REPLACE FUNCTION generate_create_table_statement(p_table_name varchar)
RETURNS text AS
$BODY$
DECLARE
v_table_ddl text;
column_record record;
BEGIN
FOR column_record IN
SELECT
b.nspname as schema_name,
b.relname as table_name,
a.attname as column_name,
pg_catalog.format_type(a.atttypid, a.atttypmod) as column_type,
CASE WHEN
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) IS NOT NULL THEN
'DEFAULT '|| (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
ELSE
''
END as column_default_value,
CASE WHEN a.attnotnull = true THEN
'NOT NULL'
ELSE
'NULL'
END as column_not_null,
a.attnum as attnum,
e.max_attnum as max_attnum
FROM
pg_catalog.pg_attribute a
INNER JOIN
(SELECT c.oid,
n.nspname,
c.relname
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname ~ ('^('||p_table_name||')$')
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3) b
ON a.attrelid = b.oid
INNER JOIN
(SELECT
a.attrelid,
max(a.attnum) as max_attnum
FROM pg_catalog.pg_attribute a
WHERE a.attnum > 0
AND NOT a.attisdropped
GROUP BY a.attrelid) e
ON a.attrelid=e.attrelid
WHERE a.attnum > 0
AND NOT a.attisdropped
ORDER BY a.attnum
LOOP
IF column_record.attnum = 1 THEN
v_table_ddl:='CREATE TABLE '||column_record.schema_name||'.'||column_record.table_name||' (';
ELSE
v_table_ddl:=v_table_ddl||',';
END IF;
IF column_record.attnum <= column_record.max_attnum THEN
v_table_ddl:=v_table_ddl||chr(10)||
' '||column_record.column_name||' '||column_record.column_type||' '||column_record.column_default_value||' '||column_record.column_not_null;
END IF;
END LOOP;
v_table_ddl:=v_table_ddl||');';
RETURN v_table_ddl;
END;
$BODY$
LANGUAGE 'plpgsql' COST 100.0 SECURITY INVOKER;
Here is the function usage:
SELECT generate_create_table_statement('tablename');
And here is the drop statement if you don't want this function to persist permanently:
DROP FUNCTION generate_create_table_statement(p_table_name varchar);

Generate the create table statement for a table in postgresql from linux commandline:
Create a table for a demo:
CREATE TABLE your_table(
thekey integer NOT NULL,
ticker character varying(10) NOT NULL,
date_val date,
open_val numeric(10,4) NOT NULL
);
pg_dump manual, can output the table create psql statement:
pg_dump -U your_user your_database -t your_table --schema-only
Which prints:
-- pre-requisite database and table configuration omitted
CREATE TABLE your_table (
thekey integer NOT NULL,
ticker character varying(10) NOT NULL,
date_val date,
open_val numeric(10,4) NOT NULL
);
-- post-requisite database and table configuration omitted
Explanation:
pg_dump helps us get information about the database itself. -U stands for username. My pgadmin user has no password set, so I don't have to put in a password. The -t option means specify for one table. --schema-only means print only data about the table, and not the data in the table.
pg_dump is elite C code that tries to play nicely with the evolving sql standards, and takes care of the thousand details that arise between postgresql's query language, and its representation on a disk. If you want to roll your own "psql disk to create statement" arrangement, ye be dragons: https://doxygen.postgresql.org/pg__dump_8c_source.html
Another option to get around pg_dump is to save the table-create SQL statement when you create the table. Keep it somewhere safe and retrieve it when you need it.
Or get the table name, column name and datatype information from postgresql with SQL:
CREATE TABLE your_table( thekey integer NOT NULL,
ticker character varying(10) NOT NULL,
date_val date,
open_val numeric(10,4) NOT NULL
);
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_name = 'your_table';
Which prints:
┌────────────┬─────────────┬───────────────────┐
│ table_name │ column_name │ data_type │
├────────────┼─────────────┼───────────────────┤
│ your_table │ thekey │ integer │
│ your_table │ ticker │ character varying │
│ your_table │ date_val │ date │
│ your_table │ open_val │ numeric │
└────────────┴─────────────┴───────────────────┘

If you want to find the create statement for a table without using pg_dump, This query might work for you (change 'tablename' with whatever your table is called):
SELECT
'CREATE TABLE ' || relname || E'\n(\n' ||
array_to_string(
array_agg(
' ' || column_name || ' ' || type || ' '|| not_null
)
, E',\n'
) || E'\n);\n'
from
(
SELECT
c.relname, a.attname AS column_name,
pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
case
when a.attnotnull
then 'NOT NULL'
else 'NULL'
END as not_null
FROM pg_class c,
pg_attribute a,
pg_type t
WHERE c.relname = 'tablename'
AND a.attnum > 0
AND a.attrelid = c.oid
AND a.atttypid = t.oid
ORDER BY a.attnum
) as tabledefinition
group by relname;
when called directly from psql, it is usefult to do:
\pset linestyle old-ascii
Also, the function generate_create_table_statement in this thread works very well.

Dean Toader Just excellent!
I'd modify your code a little, to show all constraints in the table and to make possible to use regexp mask in table name.
CREATE OR REPLACE FUNCTION public.generate_create_table_statement(p_table_name character varying)
RETURNS SETOF text AS
$BODY$
DECLARE
v_table_ddl text;
column_record record;
table_rec record;
constraint_rec record;
firstrec boolean;
BEGIN
FOR table_rec IN
SELECT c.relname FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE relkind = 'r'
AND relname~ ('^('||p_table_name||')$')
AND n.nspname <> 'pg_catalog'
AND n.nspname <> 'information_schema'
AND n.nspname !~ '^pg_toast'
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY c.relname
LOOP
FOR column_record IN
SELECT
b.nspname as schema_name,
b.relname as table_name,
a.attname as column_name,
pg_catalog.format_type(a.atttypid, a.atttypmod) as column_type,
CASE WHEN
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) IS NOT NULL THEN
'DEFAULT '|| (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
ELSE
''
END as column_default_value,
CASE WHEN a.attnotnull = true THEN
'NOT NULL'
ELSE
'NULL'
END as column_not_null,
a.attnum as attnum,
e.max_attnum as max_attnum
FROM
pg_catalog.pg_attribute a
INNER JOIN
(SELECT c.oid,
n.nspname,
c.relname
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = table_rec.relname
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 2, 3) b
ON a.attrelid = b.oid
INNER JOIN
(SELECT
a.attrelid,
max(a.attnum) as max_attnum
FROM pg_catalog.pg_attribute a
WHERE a.attnum > 0
AND NOT a.attisdropped
GROUP BY a.attrelid) e
ON a.attrelid=e.attrelid
WHERE a.attnum > 0
AND NOT a.attisdropped
ORDER BY a.attnum
LOOP
IF column_record.attnum = 1 THEN
v_table_ddl:='CREATE TABLE '||column_record.schema_name||'.'||column_record.table_name||' (';
ELSE
v_table_ddl:=v_table_ddl||',';
END IF;
IF column_record.attnum <= column_record.max_attnum THEN
v_table_ddl:=v_table_ddl||chr(10)||
' '||column_record.column_name||' '||column_record.column_type||' '||column_record.column_default_value||' '||column_record.column_not_null;
END IF;
END LOOP;
firstrec := TRUE;
FOR constraint_rec IN
SELECT conname, pg_get_constraintdef(c.oid) as constrainddef
FROM pg_constraint c
WHERE conrelid=(
SELECT attrelid FROM pg_attribute
WHERE attrelid = (
SELECT oid FROM pg_class WHERE relname = table_rec.relname
) AND attname='tableoid'
)
LOOP
v_table_ddl:=v_table_ddl||','||chr(10);
v_table_ddl:=v_table_ddl||'CONSTRAINT '||constraint_rec.conname;
v_table_ddl:=v_table_ddl||chr(10)||' '||constraint_rec.constrainddef;
firstrec := FALSE;
END LOOP;
v_table_ddl:=v_table_ddl||');';
RETURN NEXT v_table_ddl;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION public.generate_create_table_statement(character varying)
OWNER TO postgres;
Now you can, for example, make the following query
SELECT * FROM generate_create_table_statement('.*');
which results like this:
CREATE TABLE public.answer (
id integer DEFAULT nextval('answer_id_seq'::regclass) NOT NULL,
questionid integer NOT NULL,
title character varying NOT NULL,
defaultvalue character varying NULL,
valuetype integer NOT NULL,
isdefault boolean NULL,
minval double precision NULL,
maxval double precision NULL,
followminmax integer DEFAULT 0 NOT NULL,
CONSTRAINT answer_pkey
PRIMARY KEY (id),
CONSTRAINT answer_questionid_fkey
FOREIGN KEY (questionid) REFERENCES question(id) ON UPDATE RESTRICT ON DELETE RESTRICT,
CONSTRAINT answer_valuetype_fkey
FOREIGN KEY (valuetype) REFERENCES answervaluetype(id) ON UPDATE RESTRICT ON DELETE RESTRICT);
for each user table.

The easiest method I can think of is to install pgAdmin 3 (found here) and use it to view your database. It will automatically generate a query that will create the table in question.

If you want to do this for various tables at once, you meed to use the -t switch multiple times (took me a while to figure out why comma separated list wasn't working). Also, can be useful to send results to an outfile or pipe to a postgres server on another machine
pg_dump -t table1 -t table2 database_name --schema-only > dump.sql
pg_dump -t table1 -t table2 database_name --schema-only | psql -h server_name database_name

Here is another solution to the old question. There have been many excellent answers to this question over the years and my attempt borrows heavily from them.
I used Andrey Lebedenko's solution as a starting point because its output was already very close to my requirements.
Features:
following common practice I have moved the foreign key constraints outside the table definition. They are now included as ALTER TABLE statements at the bottom. The reason is that a foreign key can also link to a column of the same table. In that fringe case the constraint can only be created after the table creation is completed. The create table statement would throw an error otherwise.
The layout and indenting looks nicer now (at least to my eye)
Drop command (commented out) in the header of the definition
The solution is offered here as a plpgsql function. The algorithm does however not use any procedural language. The function just wraps one single query that can be used in a pure sql context as well.
removed redundant subqueries
Identifiers are now quoted if they are identical to reserved postgresql language elements
replaced the string concatenation operator || with the appropriate string functions to improve performance, security and readability of the code.
Note: the || operator produces NULL if one of the combined strings is NULL. It should only be used when that is the desired behaviour. (check out the
usage in the code below for an example)
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;

Even more modification based on response from #vkkeeper. Added possibility to query table from the specific schema.
CREATE OR REPLACE FUNCTION public.describe_table(p_schema_name character varying, p_table_name character varying)
RETURNS SETOF text AS
$BODY$
DECLARE
v_table_ddl text;
column_record record;
table_rec record;
constraint_rec record;
firstrec boolean;
BEGIN
FOR table_rec IN
SELECT c.relname, 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 relname~ ('^('||p_table_name||')$')
ORDER BY c.relname
LOOP
FOR column_record IN
SELECT
b.nspname as schema_name,
b.relname as table_name,
a.attname as column_name,
pg_catalog.format_type(a.atttypid, a.atttypmod) as column_type,
CASE WHEN
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) IS NOT NULL THEN
'DEFAULT '|| (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
ELSE
''
END as column_default_value,
CASE WHEN a.attnotnull = true THEN
'NOT NULL'
ELSE
'NULL'
END as column_not_null,
a.attnum as attnum,
e.max_attnum as max_attnum
FROM
pg_catalog.pg_attribute a
INNER JOIN
(SELECT c.oid,
n.nspname,
c.relname
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.oid = table_rec.oid
ORDER BY 2, 3) b
ON a.attrelid = b.oid
INNER JOIN
(SELECT
a.attrelid,
max(a.attnum) as max_attnum
FROM pg_catalog.pg_attribute a
WHERE a.attnum > 0
AND NOT a.attisdropped
GROUP BY a.attrelid) e
ON a.attrelid=e.attrelid
WHERE a.attnum > 0
AND NOT a.attisdropped
ORDER BY a.attnum
LOOP
IF column_record.attnum = 1 THEN
v_table_ddl:='CREATE TABLE '||column_record.schema_name||'.'||column_record.table_name||' (';
ELSE
v_table_ddl:=v_table_ddl||',';
END IF;
IF column_record.attnum <= column_record.max_attnum THEN
v_table_ddl:=v_table_ddl||chr(10)||
' '||column_record.column_name||' '||column_record.column_type||' '||column_record.column_default_value||' '||column_record.column_not_null;
END IF;
END LOOP;
firstrec := TRUE;
FOR constraint_rec IN
SELECT conname, pg_get_constraintdef(c.oid) as constrainddef
FROM pg_constraint c
WHERE conrelid=(
SELECT attrelid FROM pg_attribute
WHERE attrelid = (
SELECT oid FROM pg_class WHERE relname = table_rec.relname
AND relnamespace = (SELECT ns.oid FROM pg_namespace ns WHERE ns.nspname = p_schema_name)
) AND attname='tableoid'
)
LOOP
v_table_ddl:=v_table_ddl||','||chr(10);
v_table_ddl:=v_table_ddl||'CONSTRAINT '||constraint_rec.conname;
v_table_ddl:=v_table_ddl||chr(10)||' '||constraint_rec.constrainddef;
firstrec := FALSE;
END LOOP;
v_table_ddl:=v_table_ddl||');';
RETURN NEXT v_table_ddl;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

Here is a single statement that will generate the DDL for a single table in a specified schema, including constraints.
SELECT 'CREATE TABLE ' || pn.nspname || '.' || pc.relname || E'(\n' ||
string_agg(pa.attname || ' ' || pg_catalog.format_type(pa.atttypid, pa.atttypmod) || coalesce(' DEFAULT ' || (
SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = pa.attrelid
AND d.adnum = pa.attnum
AND pa.atthasdef
),
'') || ' ' ||
CASE pa.attnotnull
WHEN TRUE THEN 'NOT NULL'
ELSE 'NULL'
END, E',\n') ||
coalesce((SELECT E',\n' || string_agg('CONSTRAINT ' || pc1.conname || ' ' || pg_get_constraintdef(pc1.oid), E',\n' ORDER BY pc1.conindid)
FROM pg_constraint pc1
WHERE pc1.conrelid = pa.attrelid), '') ||
E');'
FROM pg_catalog.pg_attribute pa
JOIN pg_catalog.pg_class pc
ON pc.oid = pa.attrelid
AND pc.relname = 'table_name'
JOIN pg_catalog.pg_namespace pn
ON pn.oid = pc.relnamespace
AND pn.nspname = 'schema_name'
WHERE pa.attnum > 0
AND NOT pa.attisdropped
GROUP BY pn.nspname, pc.relname, pa.attrelid;

If you have PgAdmin4, then open it. Go to your database--> schema---> table--> right click on table name whose create script you want---> Scripts---> CREATE SCRIPT

Here is a bit improved version of shekwi's query.
It generates the primary key constraint and is able to handle temporary tables:
with pkey as
(
select cc.conrelid, format(E',
constraint %I primary key(%s)', cc.conname,
string_agg(a.attname, ', '
order by array_position(cc.conkey, a.attnum))) pkey
from pg_catalog.pg_constraint cc
join pg_catalog.pg_class c on c.oid = cc.conrelid
join pg_catalog.pg_attribute a on a.attrelid = cc.conrelid
and a.attnum = any(cc.conkey)
where cc.contype = 'p'
group by cc.conrelid, cc.conname
)
select format(E'create %stable %s%I\n(\n%s%s\n);\n',
case c.relpersistence when 't' then 'temporary ' else '' end,
case c.relpersistence when 't' then '' else n.nspname || '.' end,
c.relname,
string_agg(
format(E'\t%I %s%s',
a.attname,
pg_catalog.format_type(a.atttypid, a.atttypmod),
case when a.attnotnull then ' not null' else '' end
), E',\n'
order by a.attnum
),
(select pkey from pkey where pkey.conrelid = c.oid)) as sql
from pg_catalog.pg_class c
join pg_catalog.pg_namespace n on n.oid = c.relnamespace
join pg_catalog.pg_attribute a on a.attrelid = c.oid and a.attnum > 0
join pg_catalog.pg_type t on a.atttypid = t.oid
where c.relname = :table_name
group by c.oid, c.relname, c.relpersistence, n.nspname;
Use table_name parameter to specify the name of the table.

This is the variation that works for me:
pg_dump -U user_viktor -h localhost unit_test_database -t floorplanpreferences_table --schema-only
In addition, if you're using schemas, you'll of course need to specify that as well:
pg_dump -U user_viktor -h localhost unit_test_database -t "949766e0-e81e-11e3-b325-1cc1de32fcb6".floorplanpreferences_table --schema-only
You will get an output that you can use to create the table again, just run that output in psql.

pg_dump -h XXXXXXXXXXX.us-west-1.rds.amazonaws.com -U anyuser -t tablename -s

Like the other answers mentioned, there is no built in function that does this.
Here is a function that attempts to get all of the information that would be needed to replicate the table - or to compare deployed and checked in ddl.
This function outputs:
columns (w/ precision, null/not-null, default value)
constraints
indexes
CREATE OR REPLACE FUNCTION public.show_create_table(
in_schema_name varchar,
in_table_name varchar
)
RETURNS text
LANGUAGE plpgsql VOLATILE
AS
$$
DECLARE
-- the ddl we're building
v_table_ddl text;
-- data about the target table
v_table_oid int;
-- records for looping
v_column_record record;
v_constraint_record record;
v_index_record record;
BEGIN
-- grab the oid of the table; https://www.postgresql.org/docs/8.3/catalog-pg-class.html
SELECT c.oid INTO v_table_oid
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE 1=1
AND c.relkind = 'r' -- r = ordinary table; https://www.postgresql.org/docs/9.3/catalog-pg-class.html
AND c.relname = in_table_name -- the table name
AND n.nspname = in_schema_name; -- the schema
-- throw an error if table was not found
IF (v_table_oid IS NULL) THEN
RAISE EXCEPTION 'table does not exist';
END IF;
-- start the create definition
v_table_ddl := 'CREATE TABLE ' || in_schema_name || '.' || in_table_name || ' (' || E'\n';
-- define all of the columns in the table; https://stackoverflow.com/a/8153081/3068233
FOR v_column_record IN
SELECT
c.column_name,
c.data_type,
c.character_maximum_length,
c.is_nullable,
c.column_default
FROM information_schema.columns c
WHERE (table_schema, table_name) = (in_schema_name, in_table_name)
ORDER BY ordinal_position
LOOP
v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
|| v_column_record.column_name || ' '
|| v_column_record.data_type || CASE WHEN v_column_record.character_maximum_length IS NOT NULL THEN ('(' || v_column_record.character_maximum_length || ')') ELSE '' END || ' '
|| CASE WHEN v_column_record.is_nullable = 'NO' THEN 'NOT NULL' ELSE 'NULL' END
|| CASE WHEN v_column_record.column_default IS NOT null THEN (' DEFAULT ' || v_column_record.column_default) ELSE '' END
|| ',' || E'\n';
END LOOP;
-- define all the constraints in the; https://www.postgresql.org/docs/9.1/catalog-pg-constraint.html && https://dba.stackexchange.com/a/214877/75296
FOR v_constraint_record IN
SELECT
con.conname as constraint_name,
con.contype as constraint_type,
CASE
WHEN con.contype = 'p' THEN 1 -- primary key constraint
WHEN con.contype = 'u' THEN 2 -- unique constraint
WHEN con.contype = 'f' THEN 3 -- foreign key constraint
WHEN con.contype = 'c' THEN 4
ELSE 5
END as type_rank,
pg_get_constraintdef(con.oid) as constraint_definition
FROM pg_catalog.pg_constraint con
JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
WHERE nsp.nspname = in_schema_name
AND rel.relname = in_table_name
ORDER BY type_rank
LOOP
v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
|| 'CONSTRAINT' || ' '
|| v_constraint_record.constraint_name || ' '
|| v_constraint_record.constraint_definition
|| ',' || E'\n';
END LOOP;
-- drop the last comma before ending the create statement
v_table_ddl = substr(v_table_ddl, 0, length(v_table_ddl) - 1) || E'\n';
-- end the create definition
v_table_ddl := v_table_ddl || ');' || E'\n';
-- suffix create statement with all of the indexes on the table
FOR v_index_record IN
SELECT indexdef
FROM pg_indexes
WHERE (schemaname, tablename) = (in_schema_name, in_table_name)
LOOP
v_table_ddl := v_table_ddl
|| v_index_record.indexdef
|| ';' || E'\n';
END LOOP;
-- return the ddl
RETURN v_table_ddl;
END;
$$;
example
SELECT * FROM public.show_create_table('public', 'example_table');
produces
CREATE TABLE public.example_table (
id bigint NOT NULL DEFAULT nextval('test_tb_for_show_create_on_id_seq'::regclass),
name character varying(150) NULL,
level character varying(50) NULL,
description text NOT NULL DEFAULT 'hello there!'::text,
CONSTRAINT test_tb_for_show_create_on_pkey PRIMARY KEY (id),
CONSTRAINT test_tb_for_show_create_on_level_check CHECK (((level)::text = ANY ((ARRAY['info'::character varying, 'warn'::character varying, 'error'::character varying])::text[])))
);
CREATE UNIQUE INDEX test_tb_for_show_create_on_pkey ON public.test_tb_for_show_create_on USING btree (id);

YOu can also use a free DB management tool, such as DBeaver, which allows you to view DDL for the tables, here's an example:

DataGrip has the same functionality as pgAdmin. You can right click on a table and you will see option to auto-generate create table statement.

Use this and get your output in ddl.out file
~/bin/pg_dump -p 30000 -d <db_name> -U <db_user> --schema=<schema_name> -t <table_name> --schema-only >> /tmp/ddl.out
So this will generate DDL in the path: /tmp/ddl.out

Here is a solution if you don't want to create a function and just want the query to create a basic table structure.
select 'CREATE TABLE ' || table_name ||'(' ||STRING_AGG (
column_name || ' ' || data_type ,
','
ORDER BY
table_name,
ordinal_position
) ||');'
from
information_schema.columns
where table_schema = 'public'
group by
table_name

A simple solution, in pure single SQL.
You get the idea, you may extend it to more attributes you like to show.
with c as (
SELECT table_name, ordinal_position,
column_name|| ' ' || data_type col
, row_number() over (partition by table_name order by ordinal_position asc) rn
, count(*) over (partition by table_name) cnt
FROM information_schema.columns
WHERE table_name in ('pg_index', 'pg_tables')
order by table_name, ordinal_position
)
select case when rn = 1 then 'create table ' || table_name || '(' else '' end
|| col
|| case when rn < cnt then ',' else '); ' end
from c
order by table_name, rn asc;
Output:
create table pg_index(indexrelid oid,
indrelid oid,
indnatts smallint,
indisunique boolean,
indisprimary boolean,
indisexclusion boolean,
indimmediate boolean,
indisclustered boolean,
indisvalid boolean,
indcheckxmin boolean,
indisready boolean,
indislive boolean,
indisreplident boolean,
indkey ARRAY,
indcollation ARRAY,
indclass ARRAY,
indoption ARRAY,
indexprs pg_node_tree,
indpred pg_node_tree);
create table pg_tables(schemaname name,
tablename name,
tableowner name,
tablespace name,
hasindexes boolean,
hasrules boolean,
hastriggers boolean,
rowsecurity boolean);

Another easy option was to use [HeidiSQL client][1] for PostgreSQL database.
How to go into the database tab where all the databases and tables are listed.
Click on any of the table/View which you wanted to see the DDL/create a statement of the particular table.
Now there this client do the following jobs for you for that table, on the right-hand side windows:
The first window would be for data of table
Second for your SQL Host information
Third for database-level information like which tables and what is the size
Forth which we are more concern about table/view information tab will have the create table statement readily available for you.
I can not show you in the snapshot as working with confidential data, Try it with yourself and let me know if any issues you guys found.

In pgadminIII database>>schemas>>tables>> right click on 'Your table'>>scripts>> 'Select any one (Create,Insert,Update,Delete..)'

Here is a query with some edits,
select 'CREATE TABLE ' || a.attrelid::regclass::text || '(' ||
string_agg(a.attname || ' ' || pg_catalog.format_type(a.atttypid,
a.atttypmod)||
CASE WHEN
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) IS NOT NULL THEN
' DEFAULT '|| (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
ELSE
'' END
||
CASE WHEN a.attnotnull = true THEN
' NOT NULL'
ELSE
'' END,E'\n,') || ');'
FROM pg_catalog.pg_attribute a join pg_class on a.attrelid=pg_class.oid
WHERE a.attrelid::regclass::varchar =
'TABLENAME_with_or_without_schema'
AND a.attnum > 0 AND NOT a.attisdropped and pg_class.relkind='r'
group by a.attrelid;

To generate the SQL (DDL) behind the creation of a particular table.
We can simply use this SQL query -
SHOW TABLE your_schema_name.your_table_name

Related

Select columns that are primary key or foreign key only

Assuming that I have the following table
CREATE TABLE my_table (
a TEXT NULL,
id TEXT PRIMARY KEY NOT NULL,
c TEXT NULL,
d TEXT REFERENCES other_table_1(id) NOT NULL,
e TEXT REFERENCES other_table_2(id) NOT NULL
);
I want to perform a select statement that only select important column that is primary key and foreign key only.
SELECT (...?) FROM my_table
Expected output columns only id, d, e
What is the best non-hacky way I can achieve this?
You can create a function that build the sql statement for you and after you can execute the result.
CREATE OR REPLACE FUNCTION build_select(_tbl regclass)
RETURNS text AS
$func$
SELECT format('SELECT %s FROM %s'
, string_agg(quote_ident(important_column), ', ')
, $1)
FROM (SELECT a.attname as important_column
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid
AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = $1
AND i.indisprimary
UNION
SELECT ta.attname AS important_column
FROM (
SELECT conname, conrelid, confrelid,
unnest(conkey) AS conkey, unnest(confkey) AS confkey
FROM pg_constraint
WHERE conrelid = $1
) sub
JOIN pg_attribute AS ta ON ta.attrelid = conrelid AND ta.attnum = conkey
JOIN pg_attribute AS fa ON fa.attrelid = confrelid AND fa.attnum = confkey) my_sub_query
$func$ LANGUAGE sql;
With this function, if you do:
SELECT build_select('my-schema.my-table')
Will return you:
SELECT id, d, e FROM my-schema.my-table
And you can execute this one

Get all sequences with current values

I have the following query that gets all sequences and their schemas:
SELECT sequence_schema as schema, sequence_name as sequence
FROM information_schema.sequences
WHERE sequence_schema NOT IN ('topology', 'tiger')
ORDER BY 1, 2
I would like to get the current value of each sequence name with something like select last_value from [sequence];. I have tried the following (and a couple variations), but it doesn't work because the syntax isn't correct:
DO $$
BEGIN
EXECUTE
sequence_schema as schema,
sequence_name as sequence,
last_value
FROM information_schema.sequences
LEFT JOIN (
EXECUTE 'SELECT last_value FROM ' || schema || '.' || sequence
) tmp
ORDER BY 1, 2;
END
$$;
I've found some solutions that create functions to execute text or piece together a query inside a function and return the result, but I would prefer to have a single query that I can run and modify however I like.
In Postgres 12, you can use pg_sequences:
select schemaname as schema,
sequencename as sequence,
last_value
from pg_sequences
You can rely on the function pg_sequence_last_value
SELECT nspname as schema,
relname AS sequence_name,
coalesce(pg_sequence_last_value(s.oid), 0) AS seq_last_value
FROM pg_class AS s
JOIN pg_depend AS d ON d.objid = s.oid
JOIN pg_attribute a ON d.refobjid = a.attrelid
AND d.refobjsubid = a.attnum
JOIN pg_namespace nsp ON s.relnamespace = nsp.oid
WHERE s.relkind = 'S'
AND d.refclassid = 'pg_class'::regclass
AND d.classid = 'pg_class'::regclass
AND nspname NOT IN ('topology', 'tiger')
ORDER BY 1,2 DESC;
Here's a solution that doesn't rely on pg_sequences or pg_sequence_last_value:
CREATE OR REPLACE FUNCTION get_sequences()
RETURNS TABLE (
last_value bigint,
sequence_schema text,
sequence_name text
)
LANGUAGE plpgsql AS
$func$
DECLARE
s RECORD;
BEGIN
FOR s IN SELECT t.sequence_schema, t.sequence_name
FROM information_schema.sequences t
LOOP
RETURN QUERY EXECUTE format(
'SELECT last_value, ''%1$s''::text, ''%2$s''::text FROM %1$I.%2$I',
s.sequence_schema,
s.sequence_name
);
END LOOP;
END;
$func$;
SELECT * FROM get_sequences();
That'll output a table like this:
last_value | sequence_schema | sequence_name
------------+-----------------+-------------------------------------------------------
1 | public | contact_infos_id_seq
1 | media | photos_id_seq
2006 | company | companies_id_seq
2505 | public | houses_id_seq
1 | public | purchase_numbers_id_seq
... etc
The other answers will only work if you are on a modern version of Postgres (I believe 10 or greater).

"Show Create Table" in Redshift [duplicate]

Team,
I am working on redshift ( 8.0.2 ). I would like to have DDL command in place for any object type ( table / view...) in redshift.
I have below one. but it is not giving the full text.
select s.userid,u.usename,s.starttime, s.type, rtrim(s.text) from svl_statementtext s, pg_user u
where u.usesysid = s.userid
and s.type = 'DDL'
and s.text like '%table11%'
order by s.starttime asc;
userid | usename | starttime | type | text
--------+----------------------------------------------------------------------------------------------------------------------------------+----------------------------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
143 | user11 | 2014-04-16 23:42:06.227296 | DDL | CREATE TABLE table11 ( log_time date, user_name text, database_name text, process_id integer, connection_from text, session_id text, session_line_num bigint, command_tag text, session_start_time
143 | user11 | 2014-04-16 23:42:06.234987 | DDL | CREATE TABLE table11 ( log_time date, user_name text, database_name text, process_id integer, connection_from text, session_id text, session_line_num bigint, command_tag text, session_start_time
(2 rows)
in Oracle we have DDL_METADATA.GET_DDL pkg. it will give the full text. I would like to have the same. I tried with STL_DDLTEXT. text is much trimmed.
select xid, starttime, sequence, substring(text,1,40) as text
from stl_ddltext where userid = 100 and text like '%table11%' order by xid desc, sequence;
xid | starttime | sequence | text
--------+----------------------------+----------+------------------------------------------
135475 | 2014-04-16 23:42:06.234987 | 0 | CREATE TABLE table11 ( log_time dat
135475 | 2014-04-16 23:42:06.227296 | 0 | CREATE TABLE table11 ( log_time dat
(2 rows)
I have few more doubts on the first query output. the column lenght of "usename" is too high. how to trim that. If i query pg_user, it is trimmed internally. IN oracle we can have for e.g.
" col <col_name> for a80 "
second doubt: i am getting 2 rows. actually i created only one table. Any reason for 2 rows in the output ?
for e.g. in physical postgre db, if we want to generate any ddl for one function,
we can use below.
in the below, function name is "add"
SELECT pg_catalog.pg_get_functiondef('add'::regproc);
like this, do we have any pkg in Redshift for table/views ?
Thanks
For DDL:
First create the admin view here: https://github.com/awslabs/amazon-redshift-utils/blob/master/src/AdminViews/v_generate_tbl_ddl.sql
Next write a query like this:
select ddl
from admin.v_generate_tbl_ddl
where schemaname = 'some_schema' and tablename='some_table'
order by seq asc
I have not found a single function in Redshift that provides this functionality. You can get the full definition of views by using the pg_get_viewdef function:
SELECT 'create view '|| nc.nspname::information_schema.sql_identifier ||'.'|| c.relname::information_schema.sql_identifier ||' as '||
pg_get_viewdef(c.oid)::information_schema.character_data AS view_definition
FROM pg_namespace nc, pg_class c, pg_user u
WHERE c.relnamespace = nc.oid AND u.usesysid = c.relowner AND c.relkind = 'v'::"char"
AND nc.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
For table definitions I've put together a query, but it still needs a little work to fill in some details as noted in the commented lines:
select tm.schemaname||'.'||tm.tablename, 'create table '||tm.schemaname||'.'||tm.tablename
||' ('
||cp.coldef
-- primary key
-- diststyle
-- dist key
||d.distkey
--sort key
|| (select
' sortkey(' ||substr(array_to_string(
array( select ','||cast(column_name as varchar(100)) as str from
(select column_name from information_schema.columns col where col.table_schema= tm.schemaname and col.table_name=tm.tablename) c2
join
(-- gives sort cols
select attrelid as tableid, attname as colname, attsortkeyord as sort_col_order from pg_attribute pa where
pa.attnum > 0 AND NOT pa.attisdropped AND pa.attsortkeyord > 0
) st on tm.tableid=st.tableid and c2.column_name=st.colname order by sort_col_order
)
,'')
,2,10000) || ')'
)
||';'
from
-- t master table list
(
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid
FROM pg_namespace n, pg_class c
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
AND c.relname <> 'temp_staging_tables_1'
-- and c.relname in ('f_recipient_registration','ht_base_document','ht_folder','ht_logical_file','ht_transaction_addresses','ht_ysi_batch','ht_ysi_batch_messages','ht_ysi_files')
) tm
-- cp creates the col params for the create string
join
(select
substr(str,(charindex('QQQ',str)+3),(charindex('ZZZ',str))-(charindex('QQQ',str)+3)) as tableid
,substr(replace(replace(str,'ZZZ',''),'QQQ'||substr(str,(charindex('QQQ',str)+3),(charindex('ZZZ',str))-(charindex('QQQ',str)+3)),''),2,10000) as coldef
from
( select array_to_string(array(
SELECT 'QQQ'||cast(t.tableid as varchar(10))||'ZZZ'|| ','||column_name||' '|| decode(udt_name,'bpchar','char',udt_name) || decode(character_maximum_length,null,'', '('||cast(character_maximum_length as varchar(9))||')' )
-- default
|| decode(substr(column_default,2,8),'identity','',null,'',' default '||column_default||' ')
-- nullable
|| decode(is_nullable,'YES',' NULL ','NO',' NOT NULL ')
-- identity
|| decode(substr(column_default,2,8),'identity',' identity('||substr(column_default,(charindex('''',column_default)+1), (length(column_default)-charindex('''',reverse(column_default))-charindex('''',column_default) ) ) ||') ', '') as str
from
-- ci all the col info
(
select cast(t.tableid as int), cast(table_schema as varchar(100)), cast(table_name as varchar(100)), cast(column_name as varchar(100)),
cast(ordinal_position as int), cast(column_default as varchar(100)), cast(is_nullable as varchar(20)) , cast(udt_name as varchar(50)) ,cast(character_maximum_length as int),
sort_col_order , decode(d.colname,null,0,1) dist_key
from (select * from information_schema.columns c where c.table_schema= t.schemaname and c.table_name=t.tablename) c
left join
(-- gives sort cols
select attrelid as tableid, attname as colname, attsortkeyord as sort_col_order from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attsortkeyord > 0
) s on t.tableid=s.tableid and c.column_name=s.colname
left join
-- gives dist col
(select attrelid as tableid, attname as colname from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attisdistkey = 't'
) d on t.tableid=d.tableid and c.column_name=d.colname
order by ordinal_position
) ci
-- for the working array funct
), '') as str
from
(-- need tableid
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid
FROM pg_namespace n, pg_class c
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
) t
-- for the agg functions that dont exist
-- ) group by table_schema, table_name
)) cp on tm.tableid=cp.tableid
-- add in primary key query here
-- dist key
left join
( select
-- close off the col defs after the primary key
')' ||
' distkey('|| cast(column_name as varchar(100)) ||')' as distkey, t.tableid
from information_schema.columns c
join
(-- need tableid
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid
FROM pg_namespace n, pg_class c
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
) t on c.table_schema= t.schemaname and c.table_name=t.tablename
join
-- gives dist col
(select attrelid as tableid, attname as colname from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attisdistkey = 't'
) d on t.tableid=d.tableid and c.column_name=d.colname
) d on tm.tableid=d.tableid

Get table schema in Redshift

Hello I am trying to retrieve the schema of an existing table. I am mysql developer and am trying to work with amazon redshift. How can I export the schema of an existing table. In mysql we can use the show create table command.
SHOW CREATE TABLE tblName;
Recently I wrote a python script to clone table schemas between redshift clusters. If you only want the columns and column types of a table, you can do it via:
select column_name,
case
when data_type = 'integer' then 'integer'
when data_type = 'bigint' then 'bigint'
when data_type = 'smallint' then 'smallint'
when data_type = 'text' then 'text'
when data_type = 'date' then 'date'
when data_type = 'real' then 'real'
when data_type = 'boolean' then 'boolean'
when data_type = 'double precision' then 'float8'
when data_type = 'timestamp without time zone' then 'timestamp'
when data_type = 'character' then 'char('||character_maximum_length||')'
when data_type = 'character varying' then 'varchar('||character_maximum_length||')'
when data_type = 'numeric' then 'numeric('||numeric_precision||','||numeric_scale||')'
else 'unknown'
end as data_type,
is_nullable,
column_default
from information_schema.columns
where table_schema = 'xxx' and table_name = 'xxx' order by ordinal_position
;
But if you need the compression types and distkey/sortkeys, you need to query another table:
select * from pg_table_def where tablename = 'xxx' and schemaname='xxx';
This query will give you the complete schema definition including the Redshift specific attributes distribution type/key, sort key, primary key, and column encodings in the form of a create statement as well as providing an alter table statement that sets the owner to the current owner. The only thing it can't tell you are foreign keys. I'm working on the latter, but there's a current privilege issue in RS that prevents us from querying the right tables. This query could use some tuning, but I haven't had time or the need to work it further.
select pk.pkey, tm.schemaname||'.'||tm.tablename, 'create table '||tm.schemaname||'.'||tm.tablename
||' ('
||cp.coldef
-- primary key
||decode(pk.pkey,null,'',pk.pkey)
-- diststyle and dist key
||decode(d.distkey,null,') diststyle '||dist_style||' ',d.distkey)
--sort key
|| (select decode(skey,null,'',skey) from (select
' sortkey(' ||substr(array_to_string(
array( select ','||cast(column_name as varchar(100)) as str from
(select column_name from information_schema.columns col where col.table_schema= tm.schemaname and col.table_name=tm.tablename) c2
join
(-- gives sort cols
select attrelid as tableid, attname as colname, attsortkeyord as sort_col_order from pg_attribute pa where
pa.attnum > 0 AND NOT pa.attisdropped AND pa.attsortkeyord > 0
) st on tm.tableid=st.tableid and c2.column_name=st.colname order by sort_col_order
)
,'')
,2,10000) || ')' as skey
))
||';'
-- additional alter table queries here to set owner
|| 'alter table '||tm.schemaname||'.'||tm.tablename||' owner to "'||tm.owner||'";'
from
-- t master table list
(
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid ,use2.usename as owner, decode(c.reldiststyle,0,'EVEN',1,'KEY',8,'ALL') as dist_style
FROM pg_namespace n, pg_class c, pg_user use2
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
AND c.relname <> 'temp_staging_tables_1'
and c.relowner = use2.usesysid
) tm
-- cp creates the col params for the create string
join
(select
substr(str,(charindex('QQQ',str)+3),(charindex('ZZZ',str))-(charindex('QQQ',str)+3)) as tableid
,substr(replace(replace(str,'ZZZ',''),'QQQ'||substr(str,(charindex('QQQ',str)+3),(charindex('ZZZ',str))-(charindex('QQQ',str)+3)),''),2,10000) as coldef
from
( select array_to_string(array(
SELECT 'QQQ'||cast(t.tableid as varchar(10))||'ZZZ'|| ','||column_name||' '|| decode(udt_name,'bpchar','char',udt_name) || decode(character_maximum_length,null,'', '('||cast(character_maximum_length as varchar(9))||')' )
-- default
|| decode(substr(column_default,2,8),'identity','',null,'',' default '||column_default||' ')
-- nullable
|| decode(is_nullable,'YES',' NULL ','NO',' NOT NULL ')
-- identity
|| decode(substr(column_default,2,8),'identity',' identity('||substr(column_default,(charindex('''',column_default)+1), (length(column_default)-charindex('''',reverse(column_default))-charindex('''',column_default) ) ) ||') ', '')
-- encoding
|| decode(enc,'none','',' encode '||enc)
as str
from
-- ci all the col info
(
select cast(t.tableid as int), cast(table_schema as varchar(100)), cast(table_name as varchar(100)), cast(column_name as varchar(100)),
cast(ordinal_position as int), cast(column_default as varchar(100)), cast(is_nullable as varchar(20)) , cast(udt_name as varchar(50)) ,cast(character_maximum_length as int),
sort_col_order , decode(d.colname,null,0,1) dist_key , e.enc
from
(select * from information_schema.columns c where c.table_schema= t.schemaname and c.table_name=t.tablename) c
left join
(-- gives sort cols
select attrelid as tableid, attname as colname, attsortkeyord as sort_col_order from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attsortkeyord > 0
) s on t.tableid=s.tableid and c.column_name=s.colname
left join
(-- gives encoding
select attrelid as tableid, attname as colname, format_encoding(a.attencodingtype::integer) AS enc from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped
) e on t.tableid=e.tableid and c.column_name=e.colname
left join
-- gives dist col
(select attrelid as tableid, attname as colname from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attisdistkey = 't'
) d on t.tableid=d.tableid and c.column_name=d.colname
order by ordinal_position
) ci
-- for the working array funct
), '') as str
from
(-- need tableid
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid
FROM pg_namespace n, pg_class c
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
) t
)) cp on tm.tableid=cp.tableid
-- primary key query here
left join
(select c.oid as tableid, ', primary key '|| substring(pg_get_indexdef(indexrelid),charindex('(',pg_get_indexdef(indexrelid))-1 ,60) as pkey
from pg_index i , pg_namespace n, pg_class c
where i.indisprimary=true
and i.indrelid =c.oid
and n.oid = c.relnamespace
) pk on tm.tableid=pk.tableid
-- dist key
left join
( select
-- close off the col defs after the primary key
')' ||
' distkey('|| cast(column_name as varchar(100)) ||')' as distkey, t.tableid
from information_schema.columns c
join
(-- need tableid
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid
FROM pg_namespace n, pg_class c
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
) t on c.table_schema= t.schemaname and c.table_name=t.tablename
join
-- gives dist col
(select attrelid as tableid, attname as colname from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attisdistkey = 't'
) d on t.tableid=d.tableid and c.column_name=d.colname
) d on tm.tableid=d.tableid
where tm.schemaname||'.'||tm.tablename='myschema.mytable'
If you want to get the table structure with create statement, constraints and triggers, you can use pg_dump utility
pg_dump -U user_name -s -t table_name -d db_name
Note: -s used for schema only dump
if you want to take the data only dump , you can use -a switch.
This will output the create syntax with all the constraints. Hope this will help you.
I did not find any complete solutions out there.
And wrote a python script:
https://github.com/cxmcc/redshift_show_create_table
It will work like pg_dump, plus dealing with basic redshift features, SORTKEY/DISTKEY/DISTSTYLES etc.
As show table doesn't work on Redshift:
show table <YOUR_TABLE>;
ERROR: syntax error at or near "<YOUR_TABLE>"
We can use pg_table_def table to get the schema out:
select "column", type, encoding, distkey, sortkey, "notnull"
from pg_table_def
where tablename = '<YOUR_TABLE>';
NOTE: If the schema is not on the search path, add it to search path using:
set search_path to '$user', 'public', '<YOUR_SCHEMA>';
For redshift please try
show table <**tablename**> ;
In Postgres, you'd query the catalog.
From with psql use the shorthands to a variety of commands whose list you'll get by using \? (for help). Therefor, either of:
\d yourtable
\d+ yourtable
For use in an app, you'll need to learn the relevant queries involved. It's relatively straightforward by running psql -E (for echo hidden queries) instead of plain psql.
If you need the precise create table statement, see #Anant answer.
One easy way to do this is to use the utility provided by AWS. All you need to do is to create the view in your database and then query that view to get any table ddl. The advantage to use this view is that it will give you the sortkey and distkey as well which was used in original create table command.
https://github.com/awslabs/amazon-redshift-utils/blob/master/src/AdminViews/v_generate_tbl_ddl.sql
Once the view is created, to get the the ddl of any table. You need to query like this -
select ddl from table where tablename='table_name' and schemaname='schemaname';
Note: Admin schema might not be already there in your cluster. So you can create this view in public schema.
Below query will generate the DDL of the table for you:
SELECT ddl
FROM admin.v_generate_tbl_ddl
WHERE schemaname = '<schemaname>'
AND tablename in (
'<tablename>');
Are you needing to retrieve it programatically or from the psql prompt?
In psql use : \d+ tablename
Programatically, you can query the ANSI standard INFORMATION_SCHEMA views documented here:
http://www.postgresql.org/docs/9.1/static/information-schema.html
The INFORMATION_SCHEMA.TABLES and INFORMATION_SCHEMA.COLUMNS views should have what you need.
You can use admin view provided by AWS Redshift - https://github.com/awslabs/amazon-redshift-utils/blob/master/src/AdminViews/v_generate_tbl_ddl.sql
once you have created the view you can get schema creation script by running:
select * from <db_schema>.v_generate_tbl_ddl where tablename = '<table_name>'
To get the column data and schema of a particular table:
select * from information_schema.columns where tablename='<<table_name>>'
To get the information of a table metadata fire the below query
select * from information_schema.tables where schema='<<schema_name>>'
In the new "query editor 2", you can right click on a table and select "show definition", this will place the DDL for the table in a query window.
The below command will work:
mysql > show create table test.users_info;
Redshift/postgress >pg_dump -U root-w --no-password -h 62.36.11.547 -p 5439 -s -t test.users_info ;

How to get full length of DDL for a table or any object in redshift / postgresql

Team,
I am working on redshift ( 8.0.2 ). I would like to have DDL command in place for any object type ( table / view...) in redshift.
I have below one. but it is not giving the full text.
select s.userid,u.usename,s.starttime, s.type, rtrim(s.text) from svl_statementtext s, pg_user u
where u.usesysid = s.userid
and s.type = 'DDL'
and s.text like '%table11%'
order by s.starttime asc;
userid | usename | starttime | type | text
--------+----------------------------------------------------------------------------------------------------------------------------------+----------------------------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
143 | user11 | 2014-04-16 23:42:06.227296 | DDL | CREATE TABLE table11 ( log_time date, user_name text, database_name text, process_id integer, connection_from text, session_id text, session_line_num bigint, command_tag text, session_start_time
143 | user11 | 2014-04-16 23:42:06.234987 | DDL | CREATE TABLE table11 ( log_time date, user_name text, database_name text, process_id integer, connection_from text, session_id text, session_line_num bigint, command_tag text, session_start_time
(2 rows)
in Oracle we have DDL_METADATA.GET_DDL pkg. it will give the full text. I would like to have the same. I tried with STL_DDLTEXT. text is much trimmed.
select xid, starttime, sequence, substring(text,1,40) as text
from stl_ddltext where userid = 100 and text like '%table11%' order by xid desc, sequence;
xid | starttime | sequence | text
--------+----------------------------+----------+------------------------------------------
135475 | 2014-04-16 23:42:06.234987 | 0 | CREATE TABLE table11 ( log_time dat
135475 | 2014-04-16 23:42:06.227296 | 0 | CREATE TABLE table11 ( log_time dat
(2 rows)
I have few more doubts on the first query output. the column lenght of "usename" is too high. how to trim that. If i query pg_user, it is trimmed internally. IN oracle we can have for e.g.
" col <col_name> for a80 "
second doubt: i am getting 2 rows. actually i created only one table. Any reason for 2 rows in the output ?
for e.g. in physical postgre db, if we want to generate any ddl for one function,
we can use below.
in the below, function name is "add"
SELECT pg_catalog.pg_get_functiondef('add'::regproc);
like this, do we have any pkg in Redshift for table/views ?
Thanks
For DDL:
First create the admin view here: https://github.com/awslabs/amazon-redshift-utils/blob/master/src/AdminViews/v_generate_tbl_ddl.sql
Next write a query like this:
select ddl
from admin.v_generate_tbl_ddl
where schemaname = 'some_schema' and tablename='some_table'
order by seq asc
I have not found a single function in Redshift that provides this functionality. You can get the full definition of views by using the pg_get_viewdef function:
SELECT 'create view '|| nc.nspname::information_schema.sql_identifier ||'.'|| c.relname::information_schema.sql_identifier ||' as '||
pg_get_viewdef(c.oid)::information_schema.character_data AS view_definition
FROM pg_namespace nc, pg_class c, pg_user u
WHERE c.relnamespace = nc.oid AND u.usesysid = c.relowner AND c.relkind = 'v'::"char"
AND nc.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
For table definitions I've put together a query, but it still needs a little work to fill in some details as noted in the commented lines:
select tm.schemaname||'.'||tm.tablename, 'create table '||tm.schemaname||'.'||tm.tablename
||' ('
||cp.coldef
-- primary key
-- diststyle
-- dist key
||d.distkey
--sort key
|| (select
' sortkey(' ||substr(array_to_string(
array( select ','||cast(column_name as varchar(100)) as str from
(select column_name from information_schema.columns col where col.table_schema= tm.schemaname and col.table_name=tm.tablename) c2
join
(-- gives sort cols
select attrelid as tableid, attname as colname, attsortkeyord as sort_col_order from pg_attribute pa where
pa.attnum > 0 AND NOT pa.attisdropped AND pa.attsortkeyord > 0
) st on tm.tableid=st.tableid and c2.column_name=st.colname order by sort_col_order
)
,'')
,2,10000) || ')'
)
||';'
from
-- t master table list
(
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid
FROM pg_namespace n, pg_class c
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
AND c.relname <> 'temp_staging_tables_1'
-- and c.relname in ('f_recipient_registration','ht_base_document','ht_folder','ht_logical_file','ht_transaction_addresses','ht_ysi_batch','ht_ysi_batch_messages','ht_ysi_files')
) tm
-- cp creates the col params for the create string
join
(select
substr(str,(charindex('QQQ',str)+3),(charindex('ZZZ',str))-(charindex('QQQ',str)+3)) as tableid
,substr(replace(replace(str,'ZZZ',''),'QQQ'||substr(str,(charindex('QQQ',str)+3),(charindex('ZZZ',str))-(charindex('QQQ',str)+3)),''),2,10000) as coldef
from
( select array_to_string(array(
SELECT 'QQQ'||cast(t.tableid as varchar(10))||'ZZZ'|| ','||column_name||' '|| decode(udt_name,'bpchar','char',udt_name) || decode(character_maximum_length,null,'', '('||cast(character_maximum_length as varchar(9))||')' )
-- default
|| decode(substr(column_default,2,8),'identity','',null,'',' default '||column_default||' ')
-- nullable
|| decode(is_nullable,'YES',' NULL ','NO',' NOT NULL ')
-- identity
|| decode(substr(column_default,2,8),'identity',' identity('||substr(column_default,(charindex('''',column_default)+1), (length(column_default)-charindex('''',reverse(column_default))-charindex('''',column_default) ) ) ||') ', '') as str
from
-- ci all the col info
(
select cast(t.tableid as int), cast(table_schema as varchar(100)), cast(table_name as varchar(100)), cast(column_name as varchar(100)),
cast(ordinal_position as int), cast(column_default as varchar(100)), cast(is_nullable as varchar(20)) , cast(udt_name as varchar(50)) ,cast(character_maximum_length as int),
sort_col_order , decode(d.colname,null,0,1) dist_key
from (select * from information_schema.columns c where c.table_schema= t.schemaname and c.table_name=t.tablename) c
left join
(-- gives sort cols
select attrelid as tableid, attname as colname, attsortkeyord as sort_col_order from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attsortkeyord > 0
) s on t.tableid=s.tableid and c.column_name=s.colname
left join
-- gives dist col
(select attrelid as tableid, attname as colname from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attisdistkey = 't'
) d on t.tableid=d.tableid and c.column_name=d.colname
order by ordinal_position
) ci
-- for the working array funct
), '') as str
from
(-- need tableid
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid
FROM pg_namespace n, pg_class c
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
) t
-- for the agg functions that dont exist
-- ) group by table_schema, table_name
)) cp on tm.tableid=cp.tableid
-- add in primary key query here
-- dist key
left join
( select
-- close off the col defs after the primary key
')' ||
' distkey('|| cast(column_name as varchar(100)) ||')' as distkey, t.tableid
from information_schema.columns c
join
(-- need tableid
SELECT substring(n.nspname,1,100) as schemaname, substring(c.relname,1,100) as tablename, c.oid as tableid
FROM pg_namespace n, pg_class c
WHERE n.oid = c.relnamespace
AND nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
) t on c.table_schema= t.schemaname and c.table_name=t.tablename
join
-- gives dist col
(select attrelid as tableid, attname as colname from pg_attribute a where
a.attnum > 0 AND NOT a.attisdropped AND a.attisdistkey = 't'
) d on t.tableid=d.tableid and c.column_name=d.colname
) d on tm.tableid=d.tableid