Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/89.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
具有动态列数的PostgreSQL查询_Sql_Postgresql_Pivot_Crosstab_Postgresql 9.5 - Fatal编程技术网

具有动态列数的PostgreSQL查询

具有动态列数的PostgreSQL查询,sql,postgresql,pivot,crosstab,postgresql-9.5,Sql,Postgresql,Pivot,Crosstab,Postgresql 9.5,我试图找到一种方法来返回一个列数动态的记录集。我可以编写一个查询,生成所需的列名列表: SELECT DISTINCT name FROM tests WHERE group = 'basic'; 这将返回一个短列表,如“poke”、“prod”、“hit”、“drop”等。然后我希望生成一个表,显示一系列测试,其中每个测试都在运行。每天早上,我们都会查看开发人员一直在做什么,并对其进行戳戳和戳戳,以便每天都运行每个测试。我可以静态编写此查询: SELECT (SELECT success F

我试图找到一种方法来返回一个列数动态的记录集。我可以编写一个查询,生成所需的列名列表:

SELECT DISTINCT name FROM tests WHERE group = 'basic';
这将返回一个短列表,如“poke”、“prod”、“hit”、“drop”等。然后我希望生成一个表,显示一系列测试,其中每个测试都在运行。每天早上,我们都会查看开发人员一直在做什么,并对其进行戳戳和戳戳,以便每天都运行每个测试。我可以静态编写此查询:

SELECT (SELECT success FROM test_results AS i
        WHERE i.name = 'poke'
        AND i.date = o.date) AS 'poke',
       (SELECT success FROM test_results AS i
        WHERE i.name = 'prod'
        AND i.date = o.date) AS 'prod',
...
FROM test_results AS o GROUP BY date
HAVING date > now() - '1 week'::interval;
然而,这是硬编码到我们每天运行的测试中的。如果我们现在需要每天启动设备,我们需要更新查询。如果我们决定不再需要跌落测试,那么一周后,跌落测试列应该退出报告,因为它不再出现在结果中。当只有某些日期有结果条目时,为缺少的测试返回NULL是完全可以接受的

是否有一种方法可以通过在查询中使用常规SQL从结果创建列的动态列表

我试图通过使用带有查询的
部分地建立我需要的数据,但我找不到一种方法来从动态信息正确地建立最后一行

编辑:以下是过去两天的一些样本数据:

CREATE TABLE test_results (
    name TEXT NOT NULL,
    date DATE default now() NOT NULL,
    success BOOLEAN NOT NULL
);

INSERT INTO test_results (name, date, success) VALUES ('hit',  '2017-06-20', TRUE);
INSERT INTO test_results (name, date, success) VALUES ('poke', '2017-06-20', TRUE);
INSERT INTO test_results (name, date, success) VALUES ('prod', '2017-06-20', TRUE);

INSERT INTO test_results (name, date, success) VALUES ('poke', '2017-06-21', TRUE);
INSERT INTO test_results (name, date, success) VALUES ('prod', '2017-06-21', TRUE);

INSERT INTO test_results (name, date, success) VALUES ('poke', '2017-06-22', TRUE);
INSERT INTO test_results (name, date, success) VALUES ('prod', '2017-06-22', FALSE);

INSERT INTO test_results (name, date, success) VALUES ('poke', '2017-06-23', TRUE);
INSERT INTO test_results (name, date, success) VALUES ('prod', '2017-06-23', TRUE);
INSERT INTO test_results (name, date, success) VALUES ('drop', '2017-06-23', TRUE);
如果我对2017-06-21至2017-06-23的数据范围进行查询,我希望得到如下结果,包括当时运行的所有测试的矩阵:

date        | poke   | prod   | drop
------------+--------+--------+-----
2017-06-21  | TRUE   | TRUE   | NULL
2017-06-22  | TRUE   | FALSE  | NULL
2017-06-23  | TRUE   | TRUE   | TRUE

名称poke、prod和drop都是在该时间段内一行的名称字段中找到的名称。对于没有该日期记录的任何测试的详细查询,将返回NULL。

我将描述如下查询:

SELECT tr.name, tr.date, tr.success
FROM tests t JOIN
     test_results tr
     ON t.testid = tr.testid
WHERE t.group = 'basic' AND tr.date > now() - '1 week'::interval;

您最好在应用程序级别旋转数据。

启用“tablefunc”扩展,然后使用“crosstab”函数;请参阅PG文档:。crosstab函数的参数应该是一个查询的文本,该查询生成三列:日期、测试名称和测试成功,按顺序排列。

使用了不同的方法,有些方法在这里已经提到过,如crosstab。此外,您还可以构建自己的函数,动态构建查询并返回as和其他几个方法

但所有这些都要求您预定义输出的确切数量及其数据类型

如果我理解你的情况,你不会想要你提到的:

如果我们现在需要开始每天踢设备,我们需要更新 查询

使用交叉表和其他方法的缺点几乎相同

所以有一种方法可以使用。这可能不是最好的方法,如果您可以使用
交叉表
,则可能更好
但至少我会在代码中添加注释

解决方案:

-- Function for opening cursor
CREATE OR REPLACE
FUNCTION    test_stats(
                c REFCURSOR,    -- cursor name
                sdate date,     -- start date of period wanted (included)
                edate date,     -- end date of period wanted (included)
                gtype text      -- you had in your 'tests' table some group type which I included just in case
            )
RETURNS     REFCURSOR
LANGUAGE    PLPGSQL
AS
$main$
BEGIN
    OPEN    c
    FOR
    -- Following dynamic query building can be
    -- used also if want to go with function that RETURNS TABLE
    EXECUTE format(
            '   SELECT  r.date,
                        %s
                FROM    test_results r
                WHERE   r.date BETWEEN %L AND %L
                GROUP BY 1
            ',
                -- Here we build for each 'name' own statement and 
                -- aggregate together with comma separator to feed
                -- into main query.
                -- P.S. We need to double check result unfortunately
                --      against test_results table once to get pre-filter
                --      for names in specified date range.
                --      With this we eliminate tests that for sure will
                --      not be presented in the range. In given test data
                --      this means eliminating 'hit'.
            (
                SELECT  string_agg(
                            DISTINCT format(
                                '(  SELECT  success
                                    FROM    test_results i
                                    WHERE   i.name = %1$L
                                    AND     i.date = r.date ) AS "%1$s"',
                                t.name
                            ),
                            ','
                        )
                FROM    tests t,
                LATERAL (   SELECT  array_agg( DISTINCT r.name )
                            FROM    test_results r
                            WHERE   r.date BETWEEN sdate AND edate
                        ) a( lst )
                WHERE   t.group = gtype     -- the group type is used here
                AND     t.name = ANY ( a.lst::text[] )
            ),
            sdate,      -- start date for between statement
            edate       -- end date for between statement
        );
    RETURN c;
END;
$main$;

-- Usage example:
BEGIN;
SELECT test_stats( 'teststats1', '2017-06-21'::date, '2017-06-23'::date, 'basic' );
FETCH ALL IN teststats1;
COMMIT;

-- Result (from your given test data set):
    date    | drop | poke | prod
------------+------+------+------
 2017-06-22 |      | t    | f
 2017-06-21 |      | t    | t
 2017-06-23 | t    | t    | t
(3 rows)

正如我所提到的,这不是一种完美的方法,但它确实起到了作用:)

编辑您的问题并提供示例数据和所需结果。您需要在过程中动态构建语句,然后执行该语句。我过去也做过类似的事情,但我需要在我的硬盘上查找。如果没有人给你正确的答案,我明天再看。我尝试在SQL中这样做的原因是因为我不控制应用层。它只是将SQL语句的结果显示为应用程序中的表/矩阵。
交叉表的主要缺点是:仍然需要对主查询中的字段列表进行硬编码。下面是一篇博客文章,展示了如何使用动态SQL生成字段列表,因此,硬编码是不必要的:因为实现现在已经设置为SQL本身是一种静态类型的语言,就像C或Java一样,我所要求的是不可能的。我考虑的是动态类型语言,比如Python或JavaScript。虽然各种函数可以更改其签名以匹配其上下文,但最终,类型是在编译(准备?)原始SQL语句时确定的。