还有其他方法可以选择列的动态列表吗?

A-K*_*A-K 7 postgresql ado.net dynamic-sql postgresql-9.3 pgadmin-1.18

我需要让我的用户指定他们想要选择的列列表。到目前为止,我知道实现这一目标的两种方法。

1. 使用refcursors

CREATE OR REPLACE FUNCTION selecttestwithcolumnlist(
ticker character varying, 
columnlist character varying)
  RETURNS refcursor AS
$BODY$
DECLARE 
  ref1 refcursor;
BEGIN

OPEN ref1 FOR EXECUTE 
'select ' || ColumnList || ' from Prices WHERE Ticker=$1;'
USING     Ticker;
RETURN ref1;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE
Run Code Online (Sandbox Code Playgroud)

这个函数很容易从我的 Ado.Net 客户端调用。我需要做的就是传递参数。但是,如果我想从 pgAdmin 测试此功能,则仅当我保持事务打开时,结果集才会在屏幕上打开。这很不方便。当然,将数据显示为 HTML 表格或 Excel 电子表格很容易,但这有点不方便。

2. 使用记录集

CREATE OR REPLACE FUNCTION SelectPrices(colList VARCHAR)
 RETURNS SETOF record AS
$func$
BEGIN

RETURN QUERY EXECUTE
'SELECT ' || colList || ' FROM prices ORDER BY Ticker, ASOfDate';

END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

不幸的是,这使我的客户端代码复杂化。我不能发出这样的简单 SELECT:

SELECT price,AsOfdate,ticker FROM SelectPrices('price,AsOfdate,ticker') ;
Run Code Online (Sandbox Code Playgroud)

我必须明确提供我的结果集的结构:

SELECT price,AsOfdate,ticker FROM SelectPrices('price,AsOfdate,ticker') 
AS f(price NUMERIC,AsOfdate TIMESTAMP,ticker VARCHAR);
Run Code Online (Sandbox Code Playgroud)

这是可行的,但不方便。

还有其他方法可以返回动态列列表吗?

编辑以防止 SQL 注入,我通常拆分逗号分隔的列表并将其连接到系统视图。不会返回任何不是实际列名的内容。我最初没有提到这一点,只是为了保持问题简短。

Erw*_*ter 4

另一种方法,类似于我对上一个问题提出的方法:返回一组众所周知的类型。由于您的列列表是动态的,因此为此目的创建一个临时表。这会向系统宣布类型。作为副作用,您会得到一个临时表来保存会话期间的结果 - 就像您在上一个问题中所需要的那样。

CREATE OR REPLACE FUNCTION select_prices(_tbl anyelement, _cols text)
  RETURNS SETOF anyelement
  LANGUAGE plpgsql AS
$func$
BEGIN
   RETURN QUERY EXECUTE
   'SELECT ' || colList || '
    FROM   prices
    WHERE  ...
    ORDER  BY ...';
END
$func$;
Run Code Online (Sandbox Code Playgroud)

称呼:

CREATE TEMP TABLE t (col1 int, col2 date);
SELECT * FROM select_prices(NULL::t, 'col1, col2');
Run Code Online (Sandbox Code Playgroud)

或者,将结果保留在临时表中:

INSERT INTO t
SELECT * FROM select_prices(NULL::t, 'col1, col2');
Run Code Online (Sandbox Code Playgroud)

如果您在同一会话中需要多个表,请使用序列来获取唯一的名称。看:

但是,这种方法(就像您问题中的其他两种方法一样)容易受到SQL 注入的影响。您需要确保它不会被滥用。


再次,我会尝试使用这个简单的语句:

CREATE TEMP TABLE t AS
SELECT col1, col2 FROM prices;
Run Code Online (Sandbox Code Playgroud)