【问题标题】:Can SELECT use the results of a query to form the table expression in PostgreSQL?SELECT 可以使用查询的结果来形成 PostgreSQL 中的表表达式吗?
【发布时间】:2012-04-25 07:51:00
【问题描述】:

我有一个表,其中包含我数据库中其他表的索引。

之前我遍历这个索引来执行queries on the tables indexed,,但正如你想象的那样,这对于大量表来说非常慢。

我想知道是否可以从我的索引表上的查询结果形成一个表表达式,因此使用我的索引表上查询的list of tables result 将我的查询写成一个带有 FROM 的语句。

ETA:我使用的是 PostgreSQL v9.0。我通过一次迭代一个查询来查询具有相同查询的多个表(这很慢)。如果有一种方法可以使用一个表表达式查询所有表,我想加快速度,该表表达式是从索引表中选择的所有表的列表。

我的(简化的)表是“tabledata”,其中包含相同模式表 A、B、C、D、E、F、G 的列表以及每个表的“启用”布尔值。我想查询所有“启用”为真的字母表;最好使用一个查询,而不是一次遍历它们。基本上是查询那些返回的表:

SELECT tablename FROM tabledata WHERE enabled = TRUE`

ETA2:我在下面的查询工具中输入了:

CREATE FUNCTION f_all_tables()
  RETURNS SETOF A AS 
$body$
DECLARE 
    _tbl oid;
BEGIN 
FOR _tbl IN 
    SELECT tablename::regclass
    FROM   tabledata 
    WHERE  enabled 
LOOP 
    RETURN QUERY EXECUTE  '
    SELECT * FROM ' || _tbl; 
END LOOP; 
END;

$body$ language plpgsql; 

SELECT * FROM f_all_tables();

并得到一个错误:

ERROR:  invalid input syntax for type oid: "A"
CONTEXT:  PL/pgSQL function "f_all_tables" line 6 at FOR over SELECT rows

其中Atabledata 中的第一个表名。

ETA3:

我已经实现了下面的第二个解决方案并且它有效。谢谢 示例代码:

CREATE TABLE tabledata2 
(
  tablename character(64) NOT NULL,
  enabled boolean
)
WITH (
  OIDS=FALSE
);
ALTER TABLE tabledata2
  OWNER TO postgres;

CREATE TABLE AAAAAA
(
  enabled boolean
)
WITH (
  OIDS=FALSE
);
ALTER TABLE AAAAAA
  OWNER TO postgres;
CREATE TABLE AAAAAB
(
  enabled boolean
)
WITH (
  OIDS=FALSE
);
ALTER TABLE AAAAAB
  OWNER TO postgres;

INSERT INTO tabledata2 VALUES ('AAAAAA', 'true');
INSERT INTO tabledata2 VALUES ('AAAAAB', 'true');
INSERT INTO AAAAAA VALUES ('true');
INSERT INTO AAAAAB VALUES ('false');

CREATE FUNCTION f_all_tables() 
  RETURNS SETOF AAAAAA AS  
$body$ 
DECLARE  
    _tbl regclass; 
BEGIN  
FOR _tbl IN  
    SELECT tablename::regclass 
    FROM   tabledata2  
    WHERE  enabled
LOOP  
    RETURN QUERY EXECUTE  ' 
    SELECT * FROM ' || _tbl;  
END LOOP;  
END; 

$body$ language plpgsql;  

SELECT * FROM f_all_tables(); 

【问题讨论】:

  • 欢迎来到 StackOverflow。这是一个过于模糊和笼统的问题,无法回答。您绝对没有提供有关您的数据库架构是什么样的有用信息,您当前正在做什么(除了毫无意义的描述),也没有说明您实际尝试完成什么。如果我回答“是的,有可能”,你会奖励我正确的答案吗?如果我说“不,不是”怎么办? “也许”怎么说?请编辑您的问题以提供足够的详细信息,以便有人可以尝试并提供帮助;事实上,它应该作为"不是一个真正的问题"而结束。谢谢。 :)
  • 请更具体一点:表结构、卷、SQL 查询。
  • "我有一个表,其中包含我数据库中其他表的索引" 您很可能 非常 在这里做错了什么。您要解决的问题是什么?也许我们可以想出一种方法来重新设计您存储信息的方式,这样就不需要这种结构(永远不需要)
  • 您不应该将数据类型character(64) 用于表名。那是一种空白填充类型,现在基本上没用,可能会导致错误。请改用textMore about character types in the fine manual.

标签: postgresql select dynamic-sql


【解决方案1】:

我从您的评论中得知,您所有的候选表格都共享相同的布局。

您自己已经知道,正确的方法是拥有一个包含所有行的一个表,可能还有一个额外的列来标记它们的类型。

一个表中检索行

CREATE FUNCTION f_table(int)
  RETURNS SETOF A AS
$body$
BEGIN

    RETURN QUERY EXECUTE '
    SELECT * FROM ' || tablename::regclass FROM mytables WHERE id = $1;

END;
$body$ language plpgsql;

呼叫:

SELECT * FROM f_table(42);

要点:

  • 您可以像查询表一样查询“表函数”(返回SETOF 值的函数)。

  • 您的表格共享相同的布局。因此,您可以定义返回类型,而不必在每次调用时都键入列定义列表。

  • 您必须知道 PostgreSQL 中的每个表都有一个同名的复合类型。这就是为什么您可以使用其中一个表的名称(在我的示例中为A)作为返回类型,它会起作用。

  • 我使用cast to regclass 代替quote_ident(tablename) 作为表名。它也可以防止 SQL 注入,它也适用于模式限定的表名,例如 myschma.mytablequote_ident() 是个好主意,但在这种情况下会失败。

  • 永远不能定义具有动态 SQL 的函数STABLEMore about creating functions in the manual.


所有表中检索行

(都是相同的基本类型。)
首先:纯 SQL 无法通过 动态 表列表来做到这一点。

CREATE FUNCTION f_all_tables()
  RETURNS SETOF A AS
$body$
DECLARE
    _tbl regclass;
BEGIN

FOR _tbl IN
    SELECT tablename::regclass
    FROM   tabledata
    WHERE  enabled
LOOP
    RETURN QUERY EXECUTE '
    SELECT * FROM ' || _tbl;
END LOOP;

END;
$body$ language plpgsql;

呼叫:

SELECT * FROM f_all_tables();

如果这组表的成员不变,你可以写一个大丑UNION SELECT

SELECT * FROM A
UNION ALL
SELECT * FROM B
...

UNION ALL 保留结果中的所有重复项。

【讨论】:

  • 我执行了:CREATE FUNCTION f_table() RETURNS SETOF one_of_the_tables AS $body$ BEGIN RETURN QUERY EXECUTE ' SELECT * FROM ' || tablename::regclass FROM tabledata; END; $body$ language plpgsql; Select * from f_table();,得到了错误:{truncated} ********** Error ********** ERROR: query "SELECT ' SELECT * FROM ' || tablename::regclass FROM tabledata" returned more than one row SQL state: 21000 Context: PL/pgSQL function "f_table" line 4 at RETURN QUERY我想查询多个表。
  • 抱歉格式太差了;还没想好。
  • @JohnSmith:我已经测试了我发布的代码,它适用于 PostgreSQL 9.1。你忽略了透露你的 PostgreSQL 版本,所以我假设是当前版本。您想一次查询多个表吗?还是一次一个?请根据您的具体要求编辑您的问题(在您的问题下方按edit)。您可以在那里格式化文本。不要把它放在难以阅读的 cmets 中。您可以在此处添加简短评论,以便我收到您的更改的通知。还请添加您的 PostgreSQL 版本以及相关表和列的确切名称和类型。
  • 欧文,非常感谢您的耐心和回复。我已经更新了我的问题。请让我知道哪里还不清楚。
  • @JohnSmith:我在答案中添加了另一个答案。
【解决方案2】:

类似:

SELECT * FROM (SELECT tablename FROM mytables WHERE id = 42);

?这是不可能的。

最明显的方法是使用带有 EXECUTE 的动态语句。您可以编写一个函数来获取表的名称并返回所有行的所有列,然后您需要为其指定列定义列表:

CREATE FUNCTION select_from_table(TEXT) RETURNS SETOF RECORD
STABLE LANGUAGE 'plpgsql' AS
$$
BEGIN
    RETURN QUERY EXECUTE 'SELECT * FROM ' || quote_ident($1);
END;
$$;

SELECT * FROM select_from_table('pg_namespace') AS s(nspname name, nspowner oid, nspacl aclitem[]);

你必须想想这有多丑陋。如果您的所有表都具有相同的架构,那么您可以通过返回适当的类型来清理它(而不是让调用者每次都指定又大又丑的列定义列表)。

如果你总是想以某种方式过滤,你可以这样做。

但一般来说,您还应该考虑您要完成什么,以及您是否可以用更 SQL 的方式来完成它。

【讨论】:

  • 类似:SELECT * FROM (SELECT tablename FROM mytables WHERE id = 42); ?这不太可能这是我已经尝试过并确定不起作用的方法;这基本上是我想做的。不幸的是,我不够精通,无法理解您的建议是如何运作的,因此必须对其进行研究以确定它是否符合我的要求。我要查询的所有表都具有相同的架构。我知道更多的 SQL 方法是拥有一张大表,但当我设计方案时,我计划(最终)拥有一个分布式数据库,让不同的计算机工作。
猜你喜欢
  • 2018-10-05
  • 1970-01-01
  • 2021-12-15
  • 2021-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-13
相关资源
最近更新 更多