Determine the sequences related to a given table name PostgresSQL - postgresql

I am trying to write a function to determine the sequences related to a given table name, in the query within the function, I went with something like this:
select s.relname as sec from pg_class s
join pg_depend d on d.objid=s.oid and d.classid='pg_class'::regclass
and d.refclassid='pg_class'::regclass
join pg_class t on t.oid=d.refobjid
join pg_namespace n on n.oid=t.relnamespace
join pg_attribute a on a.attrelid=t.oid and a.attnum=d.refobjsubid
where s.relkind='S' and t.relname = 'table_name'
However it doesn't seem to find all the sequences that exists, i.e. for some tables, it just doesn't find the related sequences, but for certain ones it does.

Related

PostgreSQL 12 - List sequences that do not match their related table name

I have a database with plenty of tables.
I want to tidy up relations that do not fit namewise anymore, due to name changes of the tables.
I was able to fix the constraints, but I am not able to put the lines together to list the sequences and the related columns. As pgAdmin shows under dependencies the column a sequence is connected to, it should be possible to create a SELECT to show sequences and their related column.
Try this:
SELECT a.attrelid::regclass AS table_name,
a.attname AS column_name,
pg_get_serial_sequence(a.attrelid::regclass::text, a.attname) AS sequence_name
FROM pg_attribute AS a
JOIN pg_class AS t ON a.attrelid = t.oid
WHERE t.relkind IN ('r', 'P')
AND NOT a.attisdropped
AND pg_get_serial_sequence(a.attrelid::regclass::text, a.attname) IS NOT NULL;

Delete unused indexes

I run this query for check if there are some unused indexes in my DataBase.
select
t.tablename AS "relation",
indexname,
c.reltuples AS num_rows,
pg_relation_size(quote_ident(t.tablename)::text) AS table_size,
pg_relation_size(quote_ident(indexrelname)::text) AS index_size,
idx_scan AS number_of_scans,
idx_tup_read AS tuples_read,
idx_tup_fetch AS tuples_fetched
FROM pg_tables t
LEFT OUTER JOIN pg_class c ON t.tablename=c.relname
LEFT OUTER JOIN
( SELECT c.relname AS ctablename, ipg.relname AS indexname, x.indnatts AS number_of_columns, psai.idx_scan, idx_tup_read, idx_tup_fetch, indexrelname, indisunique FROM pg_index x
JOIN pg_class c ON c.oid = x.indrelid
JOIN pg_class ipg ON ipg.oid = x.indexrelid
JOIN pg_stat_all_indexes psai ON x.indexrelid = psai.indexrelid )
AS foo
ON t.tablename = foo.ctablename
WHERE t.schemaname='public'
and idx_scan = 0
ORDER BY
--1,2
--6
5 desc
;
And I got a lot of rows where those fields are all zero:
number_of_scans,
tuples_read,
tuples_fetched
Is that mean that I can drop them? Is there a chance that that Metadata is out-of-date? How can I check it?
I'm using Postgres with version 9.6
Your query misses some uses of indexes that do not require them to be scanned:
they enforce primary key, unique and exclusion constraints
they influence statistics collection (for “expression indexes”)
Here is my gold standard query from my blog post:
SELECT s.schemaname,
s.relname AS tablename,
s.indexrelname AS indexname,
pg_relation_size(s.indexrelid) AS index_size
FROM pg_catalog.pg_stat_user_indexes s
JOIN pg_catalog.pg_index i ON s.indexrelid = i.indexrelid
WHERE s.idx_scan = 0 -- has never been scanned
AND 0 <>ALL (i.indkey) -- no index column is an expression
AND NOT EXISTS -- does not enforce a constraint
(SELECT 1 FROM pg_catalog.pg_constraint c
WHERE c.conindid = s.indexrelid)
ORDER BY pg_relation_size(s.indexrelid) DESC;
Anything that shows up there has not been used since the statistics have been reset and can be safely dropped.
There are a few caveats:
statistics collection must run (look for the “statistics collector” process and see if you have warnings about “stale statistics” in the log)
run the query against your production database
if your program is running at many sites, try it on all of them (different users have different usage patterns)
It is possible you can delete them, however you should make sure your query runs after a typical workload. That is, are there some indexes that show no usage in this query only used during certain times when specialized queries run? Month-end reporting, weekly runs, etc? We ran into this a couple of times - several large indexes didn't get used during the day but supported month-end summaries.

classoid column in pg_description

What is the "classoid" column in pg_description table? I see it changing for different kinds of objects like table, function etc.,
The PostgreSQL Official documentations says classoid is "The OID of the system catalog this object appears in", but I don't understand it.
https://www.postgresql.org/docs/8.2/static/catalog-pg-description.html
If I'm trying to insert some comments into the pg_description table, is there a specific value based on the object? Like for table --> 1259 for Function --> 1259 etc.,
If that's true, may I know where can I find that list of classoids'?
They're oid values from pg_class, which is Postgres' internal list of tables (among other things). The simplest way to work with them is via the regclass type, e.g.:
/* Show catalog table name for all entries */
SELECT classoid::regclass, * FROM pg_description
/* Show all entries referencing pg_proc (i.e. functions) */
SELECT objoid::regprocedure, * FROM pg_description WHERE classoid = 'pg_proc'::regclass
However, you really, really shouldn't insert into the catalog tables directly. There may be associated entries to add, locks to acquire, validation to do, etc., and unless you know exactly what's going on under the hood, you could easily corrupt your database.
If you want to add an entry to pg_description, use a COMMENT statement.
The official docs and the accepted answer here did not add clarity for me, so I'm going to try to restate the answer in a way that might be helpful to others.
pg_description has four columns, objoid, classoid, objsubid, and description. description has the comment, but the other three values are needed to know exactly what is being commented on.
objoid tells whatyou specific thing in the DB this comment applies to, i.e. what actual table, column, procedure. But, there is way to know what this is refering to without consulting classoid.
classoid is the type of thing being described. This is a foreign key into pg_class. The relname column in pg_class tells you the type of thing.
pg_class is somewhat confusing. If you do select * from pg_class where relname = 'my_table' you will get a result, but this is not what pg_description.classoid refers to!
pg_description.classoid should refer to the entry in pg_class with the relname of…the string 'pg_class'. That is because the type of the thing being described is a class. And, of course, in this case, you then use objoid to look up the table in pg_class.
To clarify this, suppose pg_description's classoid referred to the entry in pg_class with the value for relname of 'pg_proc' That means that objoid is a reference to pg_proc (not to pg_class).
OK, so how do you figure out what's what?
SELECT
pg_class_objoid.relname AS "Table/View Name",
pg_description.description
FROM
pg_description
LEFT JOIN
pg_class AS pg_class_objoid ON pg_description.objoid = pg_class_objoid.oid
LEFT JOIN
pg_class AS pg_class_classoid ON pg_description.classoid = pg_class_classoid.oid
WHERE
pg_class_classoid.relname = 'pg_class'
This says to join pg_description.objoid to pg_class.oid and also to make a separate join pg_description.classoid to pg_class.oid.
We then restrict the results to only those where classoid refers to the pg_class with the relname 'pg_class'.
Now, if you do this, you'll see a lot of results. You'll see comments on tables and columns. This is where objsubid comes in.
For the table comments, the objsubid is 0, thus:
SELECT
pg_class_objoid.relname AS "Table/View Name",
pg_description.description
FROM
pg_description
LEFT JOIN
pg_class AS pg_class_objoid ON pg_description.objoid = pg_class_objoid.oid
LEFT JOIN
pg_class AS pg_class_classoid ON pg_description.classoid = pg_class_classoid.oid
WHERE
pg_class_classoid.relname = 'pg_class' AND
pg_description.objsubid = 0
Will show you what you want.
For completeness, the values of objsubid that are not 0 but where classoid indicates the type of thing is a table/view, the value for objsubid should correspond to the value pg_attribute.attnum where pg_attribute.attrelid equals pg_description.objoid. Thus, to see the table and column comments with their respective names:
SELECT
pg_class_objoid.relname AS "Table/View Name",
pg_attribute.attname AS "Column Name",
pg_description.description
FROM
pg_description
LEFT JOIN
pg_class AS pg_class_objoid ON pg_description.objoid = pg_class_objoid.oid
LEFT JOIN
pg_class AS pg_class_classoid ON pg_description.classoid = pg_class_classoid.oid
LEFT JOIN
pg_attribute ON pg_attribute.attnum = pg_description.objsubid AND
pg_attribute.attrelid = pg_description.objoid
WHERE
pg_class_classoid.relname = 'pg_class'

How to introspect materialized views

I have a utility that introspects columns of tables using:
select column_name, data_type from information_schema.columns
where table_name=%s
How can I extend this to introspect columns of materialized views?
Your query carries a few shortcomings / room for improvement:
A table name is not unique inside a database, you would have to narrow down to a specific schema, or could get surprising / misleading / totally incorrect results.
It's much more effective / convenient to cast the (optionally) schema-qualified table name to regclass ... see below.
A cast to regtype gives you generic type names instead of internal ones. But that's still only the base type.
Use the system catalog information functions format_type() instead to get an exact type name including modifiers.
With the above improvements you don't need to join to additional tables. Just pg_attribute.
Dropped columns reside in the catalog until the table is vacuumed (fully). You need to exclude those.
SELECT attname, atttypid::regtype AS base_type
, format_type(atttypid, atttypmod) AS full_type
FROM pg_attribute
WHERE attrelid = 'myschema.mytable'::regclass
AND attnum > 0
AND NOT attisdropped; -- no dead columns
As an aside: the views in the information schema are only good for standard compliance and portability (rarely works anyway). If you don't plan to switch your RDBMS, stick with the catalog tables, which are much faster - and more complete, apparently.
It would seem that postgres 9.3 has left materialized views out of the information_schema. (See http://postgresql.1045698.n5.nabble.com/Re-Materialized-views-WIP-patch-td5740513i40.html for a discussion.)
The following will work for introspection:
select attname, typname
from pg_attribute a
join pg_class c on a.attrelid = c.oid
join pg_type t on a.atttypid = t.oid
where relname = %s and attnum >= 1;
The clause attnum >= 1 suppresses system columns. The type names are pg_specific this way, I guess, but good enough for my purposes.

Why does this query deadlock?

I have an application that reads the structure of an existing PostgreSQL 9.1 database, compares it against a "should be" state and updates the database accordingly. That works fine, most of the time. However, I had several instances now when reading the current database structure deadlocked. The query responsible reads the existing foreign keys:
SELECT tc.table_schema, tc.table_name, tc.constraint_name, kcu.column_name,
ccu.table_schema, ccu.table_name, ccu.column_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY'
Viewing the server status in pgAdmin shows this to be the only active query/transaction that's running on the server. Still, the query doesn't return.
The error is reproducible in a way: When I find a database that produces the error, it will produce the error every time. But not all databases produce the error. This is one mysterious bug, and I'm running out of options and ideas on what else to try or how to work around this. So any input or ideas are highly appreciated!
PS: A colleague of mine just reported he produced the same error using PostgreSQL 8.4.
I tested and found your query very slow, too. The root of this problem is that "tables" in information_schema are in fact complicated views to provide catalogs according to the SQL standard. In this particular case, matters are further complicated as foreign keys can be built on multiple columns. Your query yields duplicate rows for those cases which, I suspect, may be an undesired.
Correlated subqueries with unnest, fed to ARRAY constructors avoid the problem in my query.
This query yields the same information, just without duplicate rows and 100x faster. Also, I would venture to guarantee, without deadlocks.
Only works for PostgreSQL, not portable to other RDBMSes.
SELECT c.conrelid::regclass AS table_name
, c.conname AS fk_name
, ARRAY(SELECT a.attname
FROM unnest(c.conkey) x
JOIN pg_attribute a
ON a.attrelid = c.conrelid AND a.attnum = x) AS fk_columns
, c.confrelid::regclass AS ref_table
, ARRAY(SELECT a.attname
FROM unnest(c.confkey) x
JOIN pg_attribute a
ON a.attrelid = c.confrelid AND a.attnum = x) AS ref_columns
FROM pg_catalog.pg_constraint c
WHERE c.contype = 'f';
-- ORDER BY c.conrelid::regclass::text,2
The cast to ::regclass yields table names as seen with your current search_path. May or may not be what you want. For this query to include the absolute path (schema) for every table name you can set the search_path like this:
SET search_path = pg_catalog;
SELECT ...
To continue your session with your default search_path:
RESET search_path;
Related:
Get column names and data types of a query, table or view