当前位置: 首页 > 知识库问答 >
问题:

函数返回给定表名和列名的表

潘胤
2023-03-14

假设我在两个不同的数据库中有一个名为static的表 and

静态表包含房屋的静态信息,如house_sizeno_roomsspa

< code>houses数据库中的< code>static表具有如下列:

pool    spa house_size  sauna   no_rooms
   1    1         25    1       2
   1    0         35    1       3

< code>apartments数据库中的< code>static表具有如下列:

pool    spa house_size  sauna   
   1    1         25    1       
   1    0         35    1       

我想在不引发任何错误的情况下运行下面的查询。目前,我得到错误,因为no_rooms列不存在于apartments.public.static中。

select pool, case when spa = 1 then 1 else 0 end as has_spa,
      sauna, house_size, case when no_rooms > 2 then 1 else 0 end as rooms 
from static;

我尝试的解决方案:

WITH static_new AS (SELECT s.*     
FROM (SELECT 0 AS no_rooms) AS dummy 
LEFT JOIN LATERAL
( SELECT
      pool, spa, sauna, house_size, no_rooms
  FROM static
)  AS s on true)
SELECT * FROM static_new;

它有效,但是当涉及更多列时,此查询会变得混乱。

我在寻找什么:

> < li>

创建一个函数,该函数采用列名和表名,然后执行我在上述查询中执行的联接,并返回一个表。(应该是通用的,适用于参数中给定的列名和表名,并返回一个表。)

还有其他漂亮整洁的解决方案吗?

共有2个答案

司寇星海
2023-03-14

不要纠结于查询。向包含所有列的两个数据库添加一个视图。在第一个数据库中:

create view v_static as
    select pool, spa house_size, sauna, no_rooms
    from status;

在第二个:

create view v_static as
    select pool, spa house_size, sauna, null as no_rooms
    from status;

然后使用视图而不是基表。

澹台啸
2023-03-14

SQL是一种严格类型化的语言,Postgres函数必须声明它们的返回类型。从函数中返回可变数量的列只有通过变通方法才有可能,比如多态类型。参见:

  • 如何在PL/pgSQL中按行类型返回表

但是在您的情况下,我们不能使用行类型,因为这因数据库而异。剩下的选择是:返回匿名记录,并为每次调用提供列定义列表。我通常不建议这样做,因为每次调用都提供列定义列表可能很乏味——而且通常毫无意义。但您的可能是少数有意义的用例之一。

尽管如此,您仍然必须知道可能丢失列的数据类型。为了本演示的目的,我将假设integer。否则,您必须额外传递数据类型并相应地构建查询。

CREATE OR REPLACE FUNCTION f_dynamic_select(_tbl regclass
                                          , _cols VARIADIC text[])  -- ①
  RETURNS SETOF record     -- ② anonymous records
  LANGUAGE plpgsql AS
$func$
BEGIN
   RETURN QUERY EXECUTE    -- ③ dynamic SQL
   format(
      'SELECT %s FROM %s'  -- ④ safe against SQLi
    , (
      SELECT string_agg(COALESCE(quote_ident(a.attname)
                              , '0 AS ' || quote_ident(t.col)  -- assuming integer!
                                ), ', ' ORDER  BY t.ord) -- ⑤
      FROM   unnest(_cols) WITH ORDINALITY t(col, ord)   -- ⑤
      LEFT   JOIN pg_attribute a ON a.attrelid = _tbl    -- ⑥
                                AND a.attnum > 0
                                AND NOT a.attisdropped 
                                AND a.attname = t.col
      )
    , _tbl
   );
END
$func$;

打电话(重要!)

SELECT *
FROM   f_dynamic_select('static', 'pool', 'spa', 'sauna', 'house_size', 'no_rooms')
AS t(pool int, spa int, house_size int, sauna int, no_rooms int); -- ② column definition list

您的示例调用,使用基于以下列的表达式:

SELECT pool, case when spa = 1 then 1 else 0 end as has_spa  -- ⑦ expressions
     , sauna, house_size
     , case when no_rooms > 2 then 1 else 0 end as rooms 
FROM f_dynamic_select('static', 'pool', 'spa', 'sauna', 'house_size', 'no_rooms')
AS t(pool int, spa int, house_size int, sauna int, no_rooms int);

数据库

(1) 该函数将表名作为注册表类型。看:

    < li >作为PostgreSQL函数参数的表名

...然后是任意的列名列表——按照有意义的顺序。< code>VARIADIC对此应该很方便。参见:

  • 在单个参数中传递多个值

请注意,我们将列名作为区分大小写的单引号字符串传递。不是(双引号)标识符。

(2)这可能是我第一次建议从函数返回匿名记录 - 在[plpgsql]标签上接近1000个答案之后。手册:

如果函数已定义为返回记录数据类型,则必须存在别名或关键字AS,然后是(column_namedata_type[,…])形式的列定义列表。列定义列表必须与函数返回的列的实际数量和类型相匹配。

③动态SQL手册。

④ 对于SQL注入是安全的,因为表名作为<code>regclass</code>传递,并且<code>SELECT</code>列表使用<code>quote_ident()</code>小心地连接。请参见:

    < li >将表名和列名定义为plpgsql函数中的参数?

(5) 使用 WITH ORDINALITY 以保持列的原始顺序。看:

  • 具有元素编号的PostgreSQL unest()

(6) 左联接到系统目录pg_attribute以标识现有列。看:

  • 在PostgreSQL中选择具有特定列名的列

(7) 将基于传递的列构建的表达式移动到外部 SELECT。

免责声明:如果有必要,我只会介绍这种复杂程度。也许您毕竟可以在每个数据库中使用简单的视图?

 类似资料:
  • 问题内容: 我想知道是否可以在SQL Server中运行一个命令/存储的程序,该程序将为我提供包含表中给定数据的列的名称。 因此,如果我要查询,请给我该表中所有包含值75的列。我不希望该行。只是表中的列名…这可能吗? 问题答案: 当您对输出感到满意时,请取消注释。 因此,让我们考虑一个简单的表: 现在,基于上述代码的存储过程: 用法示例: 输出:

  • 函数接受参数。在 Go 中,函数可以返回多个“结果参数”,而不仅仅是一个值。它们可以像变量那样命名和使用。 如果命名了返回值参数,一个没有参数的return语句,会将当前的值作为返回值返回。注意,如果遇到if等代码块和返回值同名,还需要显示写出返回值。 package main import "fmt" func split(sum int) (x, y int) { x = sum *

  • 我有一个包含以下列名的dataframe(对于引用,我只提到了一行)。我希望获得中的最大size_cd。在本例中,具有最大值,即24。我想将列名称中的数字作为返回值返回。 输入数据帧: 必需输出:即后面的部分(因为此列具有最大值) 我将感谢您对此的反馈。

  • 我正在使用laravel Elount数据对象访问我的数据,命名我的表、列、外键/主键等的最佳方式是什么? 我发现,有很多命名惯例。我只是想知道哪一个最适合Laravel雄辩的模特。 我正在考虑以下命名约定: 单数表名(例如:Post) 单列名称(例如:userId-post表中的用户id) 表格名称中多个单词的驼峰式大小写(例如:PostComment、PostReview、PostPhoto)

  • 我正在尝试使用对每一行执行一个函数并创建一个考虑多列的新列,我最初使用的是,但速度非常慢。我希望我的自定义函数中的列列表是一个变量,但除非显式地列出变量名,否则无法使其工作。例如,这是可行的: 但如果我希望它使用列输入作为vars进行计算:

  • 问题内容: 在其他语言(例如R或SQL)中找到该选项的地方,但我不太确定如何在Pandas中进行此操作。 因此,我有一个具有1262列和1行的文件,并且每次出现特定值时都需要返回列标题。 例如说这个测试数据框: 我需要找到列名,例如value = 38.15。最好的方法是什么? 谢谢 问题答案: 看到只有一行,那么您可以调用结果并使用它来屏蔽列: 分解以上内容: 您也可以使用param :