PostgreSQL能够执行跨数据库查询吗?

209

根据下面的错误信息和此谷歌搜索结果,我猜答案是否定的,但是有没有办法使用PostgreSQL执行跨数据库查询呢?

databaseA=# select * from databaseB.public.someTableName;
ERROR:  cross-database references are not implemented:
 "databaseB.public.someTableName"
我正在处理一些数据, 这些数据被分成两个数据库,但实际上这些数据是共享的(一个数据库中的userid列来自于另一个数据库中的"users"表)。我不知道为什么这两个数据库不是同一个模式(schema),但无论如何也只能接受现实...
10个回答

158

注意:正如原问题所暗示的那样,如果您正在同一台机器上设置两个数据库,您可能需要创建两个模式(schemas) - 在这种情况下,您不需要任何特殊配置即可跨它们进行查询。

postgres_fdw

使用postgres_fdw(外部数据封装器)连接到本地或远程Postgres数据库中的表。

请注意,有其他流行数据源的外部数据封装器。目前,只有postgres_fdwfile_fdw是官方Postgres发行版的一部分。

对于低于9.3版本的Postgres

旧版本已不再受支持,但如果您需要在2013年之前的Postgres安装中执行此操作,则可以使用名为dblink的函数。

我从未使用过它,但它已经包含在PostgreSQL的其他部分中并得到维护。如果您使用的是Linux发行版附带的PostgreSQL版本,则可能需要安装名为postgresql-contrib的软件包。


1
需要在安装dblink之前安装postgresql-contrib吗?还是postgresql-contrib已经包含了dblink?然后OP的查询会起作用,还是你必须以不同的方式查询它? - mpen
3
据我所读,dblink无法处理需要跨越两个数据库的查询情况。 - Paul Tomblin
1
但是关于性能和简单性,当 fdw 表不是远程的时候,就像问题中一样,所有跨数据库都是本地的并且在同一个集群中?所有文档都是关于“远程数据库” (!),并且没有提到“本地数据库”... 当所有数据库都是本地的时候,是否有一些简化和/或性能提升? - Peter Krauss
1
如果您在同一 PG 服务器中使用 FDW……FDW 性能会很糟糕!我曾使用它,但看起来速度非常慢......因此,我通过 EXPLAIN ANALYZE 比较了相同的查询,结果显示 FDW 查询速度慢了约 450 倍!是的,使用同一数据库中的所有 SQL 模式拆分事物。 - Peter Krauss

66

dblink() -- 在远程数据库中执行查询

dblink在远程数据库中执行查询(通常是SELECT,但它可以是返回行的任何SQL语句)。

当给定两个文本参数时,首先将第一个参数查找为持久连接的名称;如果找到,则在该连接上执行命令。否则,第一个参数被视为与dblink_connect相同的连接信息字符串,并且指示的连接仅在此命令的持续时间内建立。

其中一个很好的示例:

SELECT * 
FROM   table1 tb1 
LEFT   JOIN (
   SELECT *
   FROM   dblink('dbname=db2','SELECT id, code FROM table2')
   AS     tb2(id int, code text);
) AS tb2 ON tb2.column = tb1.column;

注意:我提供这些信息是为了将来的参考。参考链接


6
请不要忘记创建扩展 CREATE EXTENSION IF NOT EXISTS dblink; - D0rm1nd0
1
注意:我们可以在dblink()函数的第一个参数中传递dbname、port、host、user、password属性。例如:dblink('dbname=testrds port=5432 host=testrds.rds.amazonaws.com user=testuser password=passwd', 'SELECT id, code FROM table2') AS tb2(id int, code text); - Rakesh Soni
从设计微服务的角度来看,我认为这是不可行的,因为数据库应该能够在两个不同的微服务之间独立迁移。使用这种解决方案会在属于不同微服务的数据库之间创建紧密耦合。 - Adrian Moisa

25

我之前也遇到过这个问题,对于跨数据库查询的结论和你一样。最终,我使用模式来划分表空间,这样我就可以将表组合在一起进行查询。


20
如果您来自MySQL环境,那么MySQL所称的数据库实际上是模式(CREATE SCHEMA == MySQL中的CREATE DATABASE),因此,如果您正在从使用多个数据库的MySQL迁移内容,请使用模式。 - MkV
是的,Postgres的“数据库”本质上是完整的租户。它们存在于自己的宇宙中。(我曾经见过由于MySQL将模式视为数据库而导致的可怕安全漏洞。在一个托管站点上,我们的安全人员设法以某种方式完全访问了大约400个其他数据库。那在下一个版本中得到了修补,但这是一个绝对混乱的问题) - Shayne

11

补充一些信息。

除了当前数据库,没有其他查询数据库的方法。因为PostgreSQL加载特定于数据库的系统目录,所以跨数据库查询该如何执行是不确定的。

contrib/dblink使用函数调用实现跨数据库查询。当然,客户端也可以同时连接到不同的数据库并在客户端合并结果。

PostgreSQL FAQ


6
这些额外的信息可能会产生误导作用,使用户对以上解决方案失去信心。 - johan855
2
它不仅没有误导,而且是正确的答案。其他解决方案对于许多用例来说都不合适。 - Shayne

6

是的,您可以使用DBlink(仅限于postgresql)和DBI-Link(允许外部跨数据库查询)以及TDS_LInk来允许对MS SQL服务器运行查询。

我之前使用过DB-Link和TDS-link,并取得了巨大的成功。


4

我已经尝试使用 dblinkpostgres_fdw 在 2 个不同的数据库中创建外键关系,但都没有成功。

通过阅读其他人的反馈,例如 这里这里 以及其他一些来源,似乎目前还没有办法实现此操作:

dblinkpostgres_fdw 确实允许连接和查询其他数据库中的表,这在标准 Postgres 中是不可能的,但它们不允许在不同数据库中的表之间建立外键关系。


3
如果性能很重要且大多数查询是只读的,我建议将数据复制到另一个数据库中。虽然这似乎是不必要的数据复制,但如果需要索引,则可能会有所帮助。
可以通过简单的插入触发器来完成这项工作,这些触发器进而调用dblink来更新另一个副本。还有一些完整的复制选项(如Slony),但这超出了本文讨论范围。

2

查看https://www.cybertec-postgresql.com/en/joining-data-from-multiple-postgres-databases/ [2017年发布]

现在你也可以选择使用https://prestodb.io/

你可以在PrestoDB节点上运行SQL,它会根据需要分发SQL查询。它可以连接两个不同的数据库到同一个节点,或者连接到不同主机上的不同节点。

它不支持:

DELETE
ALTER TABLE
CREATE TABLE (CREATE TABLE AS is supported)
GRANT
REVOKE
SHOW GRANTS
SHOW ROLES
SHOW ROLE GRANTS

因此,您应该仅将其用于SELECT和JOIN需求。对于上述需求,请直接连接到每个数据库。(看起来您也可以进行INSERT或UPDATE,这很好)

客户端应用程序主要使用JDBC连接到PrestoDB,但其他类型的连接也是可能的,包括Tableu兼容的Web API

这是一个由Linux Foundation和Presto Foundation管理的开源工具。

Presto Foundation的创始成员包括:Facebook、Uber、Twitter和阿里巴巴。

当前成员包括:Facebook、Uber、Twitter、阿里巴巴、Alluxio、Ahana、Upsolver和英特尔。


我应该使用Presto还是Trino? - Kevin

1

如果有人需要更深入的例子来进行跨数据库查询,这里有一个例子,它清理每个拥有 databasechangeloglock 表的数据库:

CREATE EXTENSION IF NOT EXISTS dblink;

DO 
$$
DECLARE database_name TEXT;
DECLARE conn_template TEXT;
DECLARE conn_string TEXT;
DECLARE table_exists Boolean;
BEGIN
    conn_template = 'user=myuser password=mypass dbname=';

    FOR database_name IN
        SELECT datname FROM pg_database
        WHERE datistemplate = false
    LOOP
        conn_string = conn_template || database_name;

        table_exists = (select table_exists_ from dblink(conn_string, '(select Count(*) > 0 from information_schema.tables where table_name = ''databasechangeloglock'')') as (table_exists_ Boolean));
        IF table_exists THEN
            perform dblink_exec(conn_string, 'delete from databasechangeloglock');
        END IF;     
    END LOOP;

END
$$

0
在Sybase/MSSQL Server中,逻辑组织结构为 db.user.table 在Oracle中,逻辑组织结构为 schema.table 我理解在PostgreSQL中是db.schema.table,但db不能共享
秘诀是只使用两个级别,在Sybase中使用 db.<defaultuser.>table 或在Oracle中使用 schema.table 然后在PostgreSQL中,如果需要在不同级别之间共享表,则仅使用一个数据库(不要创建多个数据库,仅使用postgres),并像Oracle一样使用多个模式:postgres.schemaX.table

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接