作为一名长期与PostgreSQL打交道的DBA,我经常需要处理各种表查询、权限管理和跨库操作。今天整理几个高频使用的实用技巧,这些都是在日常工作中经过反复验证的可靠方案。
PostgreSQL作为一款强大的开源关系型数据库,其系统表设计和权限机制非常完善。但这也意味着某些操作需要特定的查询方式才能获取准确信息。下面这些SQL语句都是我多年积累的"看家本领",特别适合需要精确控制数据库对象的场景。
在PostgreSQL中,information_schema是一个标准化的元数据视图集合,它提供了查询数据库结构的统一方式。要查询当前数据库中public模式下的所有表,最可靠的查询方式是:
sql复制SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_catalog = current_database();
注意:这里使用
current_database()函数确保只查询当前连接的数据库,避免意外查询到其他库的表信息。
如果想进一步筛选出当前用户拥有的表,可以添加table_owner条件:
sql复制SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_catalog = current_database()
AND table_owner = current_user;
这个查询特别适合在多用户环境中快速识别自己拥有的表对象。
PostgreSQL中,表的所有权信息存储在系统目录pg_class和pg_namespace中。要查询特定表的所有者,最直接的方法是:
sql复制SELECT n.nspname AS schema_name,
c.relname AS table_name,
pg_get_userbyid(c.relowner) AS table_owner
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'my_table';
这个查询会返回表所在的schema、表名以及表的拥有者。pg_get_userbyid()函数将用户OID转换为可读的用户名。
如果需要批量查询public模式下所有表的所有者:
sql复制SELECT n.nspname AS schema_name,
c.relname AS table_name,
pg_get_userbyid(c.relowner) AS table_owner
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public'
AND c.relkind = 'r'; -- 'r'表示普通表
实用技巧:
relkind字段可以过滤不同类型的数据库对象,'r'=表,'i'=索引,'v'=视图,'S'=序列等。
PostgreSQL的dblink扩展允许我们执行跨数据库服务器的查询。这是处理分布式数据时非常有用的工具。
首先在两台服务器上都需要安装dblink扩展:
sql复制CREATE EXTENSION dblink;
然后可以执行跨服务器查询:
sql复制SELECT * FROM dblink(
'dbname=target_db host=192.168.1.100 user=dbuser password=dbpass',
'SELECT id, name FROM employees WHERE department = ''IT'''
) AS remote_data(id int, name text);
对于更复杂的场景,可以创建函数封装跨库查询逻辑:
sql复制CREATE OR REPLACE FUNCTION get_remote_row_counts(condition text)
RETURNS TABLE (table_name text, row_count bigint) AS $$
DECLARE
tbl_name text;
cnt bigint;
total bigint := 0;
BEGIN
-- 创建临时表存储远程表名
CREATE TEMP TABLE remote_tables (table_name text);
-- 从远程服务器获取表列表
INSERT INTO remote_tables
SELECT * FROM dblink(
'dbname=source_db host=192.168.1.101 user=dbuser password=dbpass',
'SELECT table_name FROM information_schema.tables WHERE table_schema = ''public'''
) AS t(table_name text);
-- 遍历每个表并统计符合条件记录数
FOR tbl_name IN SELECT table_name FROM remote_tables LOOP
EXECUTE format('SELECT count(*) FROM %I WHERE %s', tbl_name, condition) INTO cnt;
total := total + cnt;
RETURN NEXT;
END LOOP;
-- 返回总行数
RETURN QUERY SELECT 'Total', total;
-- 清理临时表
DROP TABLE remote_tables;
END;
$$ LANGUAGE plpgsql;
使用示例:
sql复制-- 统计所有表中audit_status=1的记录数
SELECT * FROM get_remote_row_counts('audit_status=1');
重要提示:dblink连接使用明文密码,在生产环境中应考虑使用.pgpass文件或连接服务文件更安全地管理凭证。
使用JOIN条件删除数据是常见的清理操作:
sql复制DELETE FROM target_table
USING source_table
WHERE target_table.id = source_table.id
AND source_table.create_time < '2023-01-01';
这种语法比子查询方式通常性能更好,特别是处理大量数据时。
有时需要对一组表执行相同操作,可以使用PL/pgSQL动态生成并执行SQL:
sql复制DO $$
DECLARE
tbl record;
BEGIN
FOR tbl IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' LOOP
EXECUTE format('ANALYZE %I', tbl.tablename);
RAISE NOTICE 'Analyzed table: %', tbl.tablename;
END LOOP;
END $$;
这个匿名代码块会对public模式下的所有表执行ANALYZE操作。
检查表权限:
sql复制SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'my_table';
修改表所有者:
sql复制ALTER TABLE my_table OWNER TO new_owner;
理解PostgreSQL的系统目录对于高效管理数据库至关重要。这些系统表存储了数据库的元数据,掌握它们的结构可以解决许多管理难题。
pg_class: 存储所有"关系"的信息,包括表、索引、序列等pg_namespace: 存储schema信息pg_user: 存储用户信息pg_attribute: 存储表的列信息pg_indexes: 存储索引信息查询所有用户表及其大小:
sql复制SELECT
n.nspname AS schema,
c.relname AS table,
pg_size_pretty(pg_total_relation_size(c.oid)) AS size
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(c.oid) DESC;
查询表的列信息:
sql复制SELECT
a.attname AS column,
pg_catalog.format_type(a.atttypid, a.atttypmod) AS type,
a.attnotnull AS not_null
FROM pg_attribute a
JOIN pg_class c ON a.attrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE c.relname = 'my_table'
AND n.nspname = 'public'
AND a.attnum > 0
AND NOT a.attisdropped;
使用EXPLAIN分析查询计划:
sql复制EXPLAIN ANALYZE
SELECT * FROM large_table WHERE category = 'books';
查询未使用的索引:
sql复制SELECT
schemaname,
relname,
indexrelname,
idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0;
设置pg_stat_statements扩展:
sql复制CREATE EXTENSION pg_stat_statements;
查询最耗时的SQL:
sql复制SELECT
query,
calls,
total_exec_time,
mean_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
创建只读用户:
sql复制CREATE USER reader WITH PASSWORD 'securepass';
GRANT CONNECT ON DATABASE mydb TO reader;
GRANT USAGE ON SCHEMA public TO reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO reader;
配置审计日志:
postgresql.conf复制log_statement = 'all'
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
logging_collector = on
启用行级安全策略:
sql复制ALTER TABLE sensitive_data ENABLE ROW LEVEL SECURITY;
CREATE POLICY user_access_policy ON sensitive_data
USING (owner = current_user);
这些PostgreSQL的高级技巧在实际工作中非常实用,特别是处理复杂的数据管理任务时。掌握系统表查询和dblink的使用可以显著提高工作效率。