How can I test if a column exists in a table using an SQL statement - postgresql

Is there a simple alternative in PostgreSQL to this statement produced in Oracle?
select table_name from user_tab_columns
where table_name = myTable and column_name = myColumn;
I am then testing whether the query returns anything so as to prove the column exists.
I am aware that using psql I can find these out individually but this is required to produce a result in a program I am writing to validate that a requested attribute field exists in my database table.

Try this :
SELECT column_name
FROM information_schema.columns
WHERE table_name='your_table' and column_name='your_column';

Accepted answer is correct, but is missing the schema and nicer output (True/False):
SELECT EXISTS (SELECT 1
FROM information_schema.columns
WHERE table_schema='my_schema' AND table_name='my_table' AND column_name='my_column');

Simpler and SQLi-safe using PostgreSQL's object identifier types:
SELECT true
FROM pg_attribute
WHERE attrelid = 'myTable'::regclass -- cast to a registered class (table)
AND attname = 'myColumn'
AND NOT attisdropped -- exclude dropped (dead) columns
-- AND attnum > 0 -- exclude system columns (you may or may not want this)
System catalogs are many times faster than querying the notoriously convoluted information_schema (but still just milliseconds for a single query). See:
Get column names and data types of a query, table or view
Read about the significance of the columns in the manual.
While building dynamic SQL with the column name supplied as parameter, use quote_ident() to defend against SQL injection:
...
AND attname = quote_ident('myColumn');
Works for tables outside the search_path, too:
...
WHERE attrelid = 'mySchema.myTable'::regclass
...

Unlike Oracle, PostgreSQL supports the ANSI standard INFORMATION_SCHEMA views.
The corresponding standard view to Oracle's user_tab_columns is information_schema.columns
http://www.postgresql.org/docs/current/static/infoschema-columns.html

SELECT attname
FROM pg_attribute
WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'YOURTABLENAME')
AND attname = 'YOURCOLUMNNAME';
Of course, replace YOURTABLENAME and YOURCOLUMNNAME with the proper values. If a row is returned, a column with that name exists, otherwise it does not.

Here is a similar variant of Erwin Brandstetter answer.
Here we check schema too in case we have similar tables in different schema.
SELECT TRUE FROM pg_attribute
WHERE attrelid = (
SELECT c.oid
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE
n.nspname = CURRENT_SCHEMA()
AND c.relname = 'YOURTABLENAME'
)
AND attname = 'YOURCOLUMNNAME'
AND NOT attisdropped
AND attnum > 0

Related

Sequence exists but I can't find it in information_schema.sequences (PostgreSQL)

I have created a sequence (let's call it my_seq) in a schema (let's call it my_schema) of my PostgreSQL (version 13) database. I am sure the sequence exists because I can find it in the result set of the query
select n.nspname as sequence_schema,
c.relname as sequence_name
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where c.relkind = 'S'
and n.nspname = 'my_schema'
However, if I run the following query
select sequence_name
from information_schema.sequences
where sequence_schema = 'my_schema'
my_seq isn't in the result set.
I have run both queries with the same user I created the sequence with.
Can anybody help me find an explanation for this?
The missing sequences are likely the ones used in an Identity column.
You can fetch all sequences using select * from pg_sequences;
To answer the why of the question: information_schema.sequences is a view, you can see its definition by running \d+ information_schema.sequences. There, we can see that it filters out objects being an internal dependency (AND NOT (EXISTS ... AND pg_depend.deptype = 'i'), which is the case of the sequences backing an Identity column.

Getting a column comment in PostgreSql [duplicate]

I'm running a project on a Postgres database and need to retrieve the comments on columns within the DB to be used as table headings and such. I have seen that there are a couple of built in functions (pg_description and col_description) but i haven't been able to find examples on how to use them and playing around with them has proved pretty futile.
So I was wondering if any has been able to do this before and if so, how?
select
c.table_schema,
c.table_name,
c.column_name,
pgd.description
from pg_catalog.pg_statio_all_tables as st
inner join pg_catalog.pg_description pgd on (
pgd.objoid = st.relid
)
inner join information_schema.columns c on (
pgd.objsubid = c.ordinal_position and
c.table_schema = st.schemaname and
c.table_name = st.relname
);
It all works by oid,
mat=> SELECT c.oid FROM pg_catalog.pg_class c WHERE c.relname = 'customers';
oid
-------
23208
(1 row)
Now, I have the oid for that table, so I can ask :
mat=> select pg_catalog.obj_description(23208);
obj_description
-------------------
Customers
(1 row)
Then, I can ask for the description of the fourth column :
mat=> select pg_catalog.col_description(23208,4);
col_description
-----------------------------------------
Customer codes, CHS, FACTPOST, POWER...
(1 row)
If you want to know which queries does psql run when you do \dt+ or \d+ customers, just run it with -E.
Just to be here if somebody will need it.
There are many answers here, but none of them was as simple as I would like it to be. So, based on previous answers and current postgres 9.4, I have created this query:
SELECT
obj_description(format('%s.%s',isc.table_schema,isc.table_name)::regclass::oid, 'pg_class') as table_description,
pg_catalog.col_description(format('%s.%s',isc.table_schema,isc.table_name)::regclass::oid,isc.ordinal_position) as column_description
FROM
information_schema.columns isc
It fetches table and column descriptions, without any confusing joins and ugly string concatenations.
Take care with schemas, this code considers them:
SELECT
cols.column_name, (
SELECT
pg_catalog.col_description(c.oid, cols.ordinal_position::int)
FROM
pg_catalog.pg_class c
WHERE
c.oid = (SELECT ('"' || cols.table_name || '"')::regclass::oid)
AND c.relname = cols.table_name
) AS column_comment
FROM
information_schema.columns cols
WHERE
cols.table_catalog = 'your_database'
AND cols.table_name = 'your_table'
AND cols.table_schema = 'your_schema';
References:
Postgresql Document Table and Column Description Comments on Table and Column
Determining the OID of a table in Postgres 9.1?
A slight change to one of the other answers which only gives you columns that have comments on them, this gives you all columns whether they have a comment or not.
select c.table_schema, st.relname as TableName, c.column_name,
pgd.description
from pg_catalog.pg_statio_all_tables as st
inner join information_schema.columns c
on c.table_schema = st.schemaname
and c.table_name = st.relname
left join pg_catalog.pg_description pgd
on pgd.objoid=st.relid
and pgd.objsubid=c.ordinal_position
where st.relname = 'YourTableName';
This works for me using the PostBooks 3.2.2 DB:
select cols.column_name,
(select pg_catalog.obj_description(oid) from pg_catalog.pg_class c where c.relname=cols.table_name) as table_comment
,(select pg_catalog.col_description(oid,cols.ordinal_position::int) from pg_catalog.pg_class c where c.relname=cols.table_name) as column_comment
from information_schema.columns cols
where cols.table_catalog='postbooks' and cols.table_name='apapply'
Regards,
Sylnsr
If you just need to show the comments for your columns among other data, you can also use:
\d+ my_table
Enhance for #Nick and #mat suggestions: use
SELECT obj_description('schemaName.tableName'::regclass, 'pg_class');
when you have string name (not oid).
To avoid to remember 'pg_class' parameter, and to avoid ugly concatenations at the function calls, as (tname||'.'||schema)::regclass, an useful overload for obj_description:
CREATE FUNCTION obj_description(
p_rname text, p_schema text DEFAULT NULL,
p_catalname text DEFAULT 'pg_class'
) RETURNS text AS $f$
SELECT obj_description((CASE
WHEN strpos($1, '.')>0 OR $2 IS NULL OR $2='' THEN $1
ELSE $2||'.'||$1
END)::regclass, $3);
$f$ LANGUAGE SQL IMMUTABLE;
-- USAGE: obj_description('mytable')
-- SELECT obj_description('s.t');
-- PS: obj_description('s.t', 'otherschema') is a syntax error,
-- but not generates exception: returns the same as ('s.t')
Now is easy to use, because the table name (rname parameter) is a varchar and can be expressed with a separated field for schema name, as in the main tables and queries.
See also "Getting list of table comments in PostgreSQL" or the new pg9.3 Guide
This answer is a little late, but it popped up on a google search I did to research this problem. We only needed Table descriptions, but the method would be the same for columns.
The column descriptions are in the pg_description table also, referenced by objoid.
Add this view:
CREATE OR REPLACE VIEW our_tables AS
SELECT c.oid, n.nspname AS schemaname, c.relname AS tablename, d.description,
pg_get_userbyid(c.relowner) AS tableowner, t.spcname AS "tablespace",
c.relhasindex AS hasindexes, c.relhasrules AS hasrules, c.reltriggers > 0 AS hastriggers
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_tablespace t ON t.oid = c.reltablespace
LEFT JOIN pg_description d ON c.oid = d.objoid
WHERE c.relkind = 'r'::"char";
ALTER TABLE our_tables OWNER TO postgres;
GRANT SELECT, UPDATE, INSERT, DELETE, REFERENCES, TRIGGER ON TABLE our_tables TO postgres;
GRANT SELECT ON TABLE our_tables TO public;
Then run:
SELECT tablename, description FROM our_tables WHERE schemaname = 'public'
The view is a modified version of the pg_tables view which adds in the description column.
You could also monkey around with the view definition to make it a single query.
I accessed table comments like this:
select c.relname table_name, pg_catalog.obj_description(c.oid) as comment from pg_catalog.pg_class c where c.relname = 'table_name';
and column comments thusly:
SELECT c.column_name, pgd.description FROM pg_catalog.pg_statio_all_tables as st inner join pg_catalog.pg_description pgd on (pgd.objoid=st.relid) inner join information_schema.columns c on (pgd.objsubid=c.ordinal_position and c.table_schema=st.schemaname and c.table_name=st.relname and c.table_name = 'table_name' and c.table_schema = 'public');
I asked a similar question about Postgresql comments last month. If you dig through that, you'll come across some Perl code over on my blog that automates the process of extracting a comment.
To pull out the column names of a table, you can use something like the following:
select
a.attname as "colname"
,a.attrelid as "tableoid"
,a.attnum as "columnoid"
from
pg_catalog.pg_attribute a
inner join pg_catalog.pg_class c on a.attrelid = c.oid
where
c.relname = 'mytable' -- better to use a placeholder
and a.attnum > 0
and a.attisdropped is false
and pg_catalog.pg_table_is_visible(c.oid)
order by a.attnum
You can then use the tableoid,columnoid tuple to extract the comment of each column (see my question).
I just found this here. It will provide you with all kind of metadata on one specific table (type, default value, not null flag, length, comment, foreign key name, primary key name). It seems to work well.
SELECT pg_tables.tablename, pg_attribute.attname AS field,
format_type(pg_attribute.atttypid, NULL) AS "type",
pg_attribute.atttypmod AS len,
(SELECT col_description(pg_attribute.attrelid,
pg_attribute.attnum)) AS comment,
CASE pg_attribute.attnotnull
WHEN false THEN 1 ELSE 0
END AS "notnull",
pg_constraint.conname AS "key", pc2.conname AS ckey,
(SELECT pg_attrdef.adsrc FROM pg_attrdef
WHERE pg_attrdef.adrelid = pg_class.oid
AND pg_attrdef.adnum = pg_attribute.attnum) AS def
FROM pg_tables, pg_class
JOIN pg_attribute ON pg_class.oid = pg_attribute.attrelid
AND pg_attribute.attnum > 0
LEFT JOIN pg_constraint ON pg_constraint.contype = 'p'::"char"
AND pg_constraint.conrelid = pg_class.oid AND
(pg_attribute.attnum = ANY (pg_constraint.conkey))
LEFT JOIN pg_constraint AS pc2 ON pc2.contype = 'f'::"char"
AND pc2.conrelid = pg_class.oid
AND (pg_attribute.attnum = ANY (pc2.conkey))
WHERE pg_class.relname = pg_tables.tablename
-- AND pg_tables.tableowner = "current_user"()
AND pg_attribute.atttypid <> 0::oid
AND tablename='your_table'
ORDER BY field ASC
Source: http://golden13.blogspot.de/2012/08/how-to-get-some-information-about_7.html
Ok, so i worked it out to degree...
select col_description(table id, column number)...
ie: select col_description(36698,2);
That worked, but is there an easier way to do this maybe bringing all the comments on all the columns and using the table name instead of the oid???
To display comments from all columns of all table :
SELECT
cols.table_name,
cols.column_name, (
SELECT
pg_catalog.col_description(c.oid, cols.ordinal_position::int)
FROM
pg_catalog.pg_class c
WHERE
c.oid = (SELECT ('"' || cols.table_name || '"')::regclass::oid)
AND c.relname = cols.table_name
) AS column_comment
FROM
information_schema.columns cols
WHERE
cols.table_name IN (SELECT cols.table_name FROM information_schema.columns)
AND cols.table_catalog = 'your_database_name'
AND cols.table_schema = 'your_schema_name';
You need to execute this query outside any schema/catalog/db
This query is based on another answer in this question which display comments from one table only
To extend on the response provided by #amxy; I found that adding a schema filter can help in some environments. As I found #amxy's solution didn't work until I added by schema filters
SELECT
pg_tables.schemaname,
pg_tables.TABLENAME,
pg_attribute.attname AS field,
format_type(pg_attribute.atttypid, NULL) AS "type",
pg_attribute.atttypmod AS len,
(
SELECT col_description(pg_attribute.attrelid, pg_attribute.attnum)) AS COMMENT,
CASE pg_attribute.attnotnull
WHEN FALSE THEN 1
ELSE 0
END AS "notnull",
pg_constraint.conname AS "key", pc2.conname AS ckey,
(
SELECT pg_attrdef.adsrc
FROM pg_attrdef
WHERE pg_attrdef.adrelid = pg_class.oid
AND pg_attrdef.adnum = pg_attribute.attnum) AS def
FROM pg_tables, pg_class
JOIN pg_attribute
ON pg_class.oid = pg_attribute.attrelid
AND pg_attribute.attnum > 0
LEFT JOIN pg_constraint
ON pg_constraint.contype = 'p'::"char"
AND pg_constraint.conrelid = pg_class.oid
AND
(pg_attribute.attnum = ANY (pg_constraint.conkey))
LEFT JOIN pg_constraint AS pc2
ON pc2.contype = 'f'::"char"
AND pc2.conrelid = pg_class.oid
AND (pg_attribute.attnum = ANY (pc2.conkey))
WHERE pg_class.relname = pg_tables.TABLENAME
AND pg_tables.schemaname IN ('op', 'im', 'cs','usr','li')
-- AND pg_tables.tableowner = "current_user"()
AND pg_attribute.atttypid <> 0::oid
---AND TABLENAME='your_table'
ORDER BY pg_tables.schemaname,
pg_tables.TABLENAME ASC;
RESULTS:
SELECT
relname table_name,
obj_description(oid) table_description,
column_name,
pgd.description column_description
FROM pg_class
INNER JOIN
information_schema.columns
ON table_name = pg_class.relname
LEFT JOIN
pg_catalog.pg_description pgd
ON pgd.objsubid = ordinal_position
WHERE
relname = 'your_table_name'
SELECT sc.table_schema , sc.table_name, sc.column_name, col_description(pc."oid" , sc.ordinal_position) col_description FROM pg_class pc
INNER JOIN pg_namespace ns ON ns."oid" =pc.relnamespace
INNER JOIN information_schema.COLUMNS sc ON sc.table_name=pc.relname AND sc.table_schema=ns.nspname
WHERE 1=1
AND upper(ns.nspname) = 'TABLE_SCHEMA'
AND upper(pc.relname) = 'TABLE_NAME'
Retrieving Comments from a PostgreSQL DB

Export data types from PostgreSQL schema

I'd like to export the data types from a PostgreSQL specific schema. The problem is, for now I found just a way to export the whole schema and not just the data types.
The schema has more than 2000 tables, and I don't need all this.
Is there a way to export JUST the custom data types?
This gets you the list of available datatypes - for custom ones you probably need to filter by the first column (nspname):
SELECT n.nspname, typname, pg_catalog.format_type(t.oid, NULL) AS typefull
FROM pg_catalog.pg_type t
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid) AND
pg_catalog.pg_type_is_visible(t.oid)
Not sure if this is what you had in mind or not:
select
data_type, count (*)
from information_schema.columns
where
table_schema = 'my_schema'
group by
data_type
If by custom types ONLY you mean user-defined, then I think you would want the udt_name instead for user-defined types:
select
udt_name, count (*)
from information_schema.columns
where
table_schema = 'my_schema' and
data_type = 'USER-DEFINED'
group by
udt_name

Using query to set the column type in PostgreSQL

After the excellent answer by Alexandre GUIDET, I attempted to run the following query:
create table egg (id (SELECT
pg_catalog.format_type(a.atttypid, a.atttypmod) as Datatype
FROM
pg_catalog.pg_attribute a
WHERE
a.attnum > 0
AND NOT a.attisdropped
AND a.attrelid = (
SELECT c.oid
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname ~ '^(TABLENAME)$'
AND pg_catalog.pg_table_is_visible(c.oid)
)
and a.attname = 'COLUMNNAME'));
PostgreSQL, however, complains about incorrect syntax. Specifically it says that I cannot write: create table egg (id (SELECT.
Are there any workarounds? Can't I convert the result of a query to text and reuse it as a query?
There is a much simpler way to do that.
SELECT pg_typeof(col)::text FROM tbl LIMIT 1
Only precondition is that the template table holds at least one row. See the manual on pg_typeof()
As Milen wrote, you need to EXECUTE dynamic DDL statements like this.
A much simpler DO statement:
DO $$BEGIN
EXECUTE 'CREATE TABLE egg (id '
|| (SELECT pg_typeof(col)::text FROM tbl LIMIT 1) || ')';
END$$;
Or, if you are not sure the template table has any rows:
DO $$BEGIN
EXECUTE (
SELECT format('CREATE TABLE egg (id %s)'
, format_type(atttypid, atttypmod))
FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass -- name of template table
AND attname = 'col' -- name of template column
AND attnum > 0 AND NOT attisdropped
);
END$$;
These conditions seem redundant, since you look for a specific column any
format() requires Postgres 9.1+.
Related:
How to check if a table exists in a given schema
You can either convert that query to a function or (if you have Postgres 9.0) to an anonymous code block:
DO $$DECLARE the_type text;
BEGIN
SELECT ... AS datatype INTO the_type FROM <the rest of your query>;
EXECUTE 'create table egg ( id ' || the_type || <the rest of your create table statement>;
END$$;
You can either have a table a definition or a query, but not both. Maybe your thinking of the select into command.

pg_relation_size tells me column doesn't exist

http://www.postgresql.org/docs/8.4/static/functions-admin.html says:
pg_relation_size
accepts the OID or name of a table, index or toast table, and returns the size in bytes
However when I use it with a valid table name, I get the error:
column [table] does not exist...
I know my table exists, because doing
SELECT count(*) FROM [table]
returns a valid number. Any ideas?
I got the same error though the cause was different. pg_relation_size is case insensitive, so if you have anything other than lower case it will not work out of the box:
postgres=> SELECT pg_size_pretty(pg_total_relation_size('MyTable'));
ERROR: relation "mytable" does not exist
LINE 1: SELECT pg_size_pretty(pg_total_relation_size('mytable...
^
postgres=> SELECT pg_size_pretty(pg_total_relation_size('"MyTable"'));
pg_size_pretty
----------------
225 MB
(1 row)
So in order for this to work in a SELECT statement you need to enclose the table name in quotes:
postgres=> SELECT relname, nspname, pg_size_pretty(pg_relation_size('"' || relname || '"'))
FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r','') AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND pg_table_is_visible(c.oid)
ORDER BY c.relpages DESC;
Try explicitely adding the schema (e.g. 'public') where the table is located in the pg_relation_size call.
Like this (untested):
select pg_relation_size(public.mytablename) from pg_tables