根据默认订单加入2套

时间:2013-07-14 18:57:46

标签: postgresql

如何仅根据默认订单加入2组记录?

所以,如果我有一个表x(col(1,2,3,4,5,6,7))和另一个表z(col(a,b,c,d,e,f,g))

它会返回

c1 c2
-- --    
1   a
2   b
3   c
4   d
5   e
6   f
7   g

实际上,我想从参数中加入一对一维数组,并将它们视为表中的列。

示例代码:

CREATE OR REPLACE FUNCTION "Test"(timestamp without time zone[],
                                  timestamp without time zone[])
  RETURNS refcursor AS
$BODY$
DECLARE
curr refcursor;
BEGIN
    OPEN curr FOR 
        SELECT DISTINCT "Start" AS x, "End" AS y, COUNT("A"."id") 
        FROM UNNEST($1) "Start" 
        INNER JOIN 
        (
            SELECT "End", ROW_NUMBER() OVER(ORDER BY ("End")) rn
                FROM UNNEST($2) "End" ORDER BY ("End") 
        ) "End" ON ROW_NUMBER() OVER(ORDER BY ("Start")) = "End".rn 
        LEFT JOIN "A" ON ("A"."date" BETWEEN x AND y) 
        GROUP BY 1,2 
        ORDER BY "Start";
    return curr;
END

$BODY$

3 个答案:

答案 0 :(得分:3)

现在,回答评论中显示的真实问题,这似乎是:

  

鉴于两个数组'a'和'b',我如何配对它们的元素,以便我可以将元素对作为查询中的列别名?

有几种方法可以解决这个问题:

  • 当且仅当数组长度相等时,在unnest子句中使用多个SELECT函数(不推荐使用的方法只能用于向后兼容);

  • 使用generate_subscripts循环遍历数组;

  • 如果您需要支持的版本太旧而不能generate_series,请使用array_lowerarray_uppergenerate_subscripts的子查询进行模拟generate_subscripts / p>

  • 依赖于unnest返回元组和希望的顺序 - 就像我的其他答案一样,如下所示。它会起作用,但不能保证在将来的版本中有效。

  • 当9.4出现时,使用the WITH ORDINALITY functionality added in PostgreSQL 9.4(另请参阅its first posting)获取unnest的行号。

  • 使用多列数组UNNEST,这是SQL标准但which PostgreSQL doesn't support yet

所以,假设我们的函数arraypair包含数组参数ab

CREATE OR REPLACE FUNCTION arraypair (a integer[], b text[]) 
RETURNS TABLE (col_a integer, col_b text) AS $$
  -- blah code here blah
$$ LANGUAGE whatever IMMUTABLE;

并将其调用为:

SELECT * FROM arraypair( ARRAY[1,2,3,4,5,6,7], ARRAY['a','b','c','d','e','f','g'] );

可能的函数定义是:

SRF-in - SELECT(已弃用)

CREATE OR REPLACE FUNCTION arraypair (a integer[], b text[])
RETURNS TABLE (col_a integer, col_b text) AS $$
    SELECT unnest(a), unnest(b);
$$ LANGUAGE sql IMMUTABLE;

如果数组的长度不相等,会产生奇怪和意外的结果;请参阅SELECT列表中有关设置返回函数及其非标准用法的文档,以了解原因以及确切发生的情况。

generate_subscripts

这可能是最安全的选择:

CREATE OR REPLACE FUNCTION arraypair (a integer[], b text[])
RETURNS TABLE (col_a integer, col_b text) AS $$
    SELECT
       a[i], b[i]
    FROM generate_subscripts(CASE WHEN array_length(a,1) >= array_length(b,1) THEN a::text[] ELSE b::text[] END, 1) i;
$$ LANGUAGE sql IMMUTABLE;

如果数组长度不等,那么写入时它将返回较短的null元素,因此它的工作方式就像一个完整的外连接。颠倒案例的意义以获得类似内部联接的效果。该函数假定数组是一维的,并且它们从索引1开始。如果整个数组参数为NULL,则该函数返回NULL。

更普遍的版本将用PL / PgSQL编写,并检查array_ndims(a) = 1,检查array_lower(a, 1) = 1,测试空数组等。我会留给你。

希望成对返回:

这不能保证工作,但PostgreSQL的当前查询执行器可以使用:

CREATE OR REPLACE FUNCTION arraypair (a integer[], b text[])
RETURNS TABLE (col_a integer, col_b text) AS $$
 WITH
    rn_c1(rn, col) AS (
      SELECT row_number() OVER (), c1.col
      FROM unnest(a) c1(col) 
    ),
    rn_c2(rn, col) AS (
      SELECT row_number() OVER (), c2.col
      FROM unnest(b) c2(col)
    )
    SELECT
      rn_c1.col AS c1, 
      rn_c2.col AS c2
    FROM rn_c1 
    INNER JOIN rn_c2 ON (rn_c1.rn = rn_c2.rn);
$$ LANGUAGE sql IMMUTABLE;

我会考虑使用generate_subscripts更安全。

多参数unnest

这个应该有效,但不是因为PostgreSQL的unnest不接受多个输入数组(

SELECT * FROM unnest(a,b);

答案 1 :(得分:1)

select x.c1, z.c2
from
    x
    inner join
    (
        select
            c2,
            row_number() over(order by c2) rn
        from z
        order by c2
    ) z on x.c1 = z.rn
order by x.c1

如果x.c1不是1,2,3...,您可以执行与使用z

相同的操作

Erwin指出中间order by不是必需的。我测试了这样:

create table t (i integer);
insert into t
select ceil(random() * 100000)
from generate_series(1, 100000);

select
    i,
    row_number() over(order by i) rn
from t
;

而且i已经订购了。在这个我从未执行过的简单测试之前,我可能会按任何顺序对行进行编号。

答案 2 :(得分:0)

通过“默认顺序”,听起来您可能是select * from tablename在没有ORDER BY的情况下返回行的顺序。

如果是,则此顺序为 undefined 。数据库可以按任何顺序返回行。你会发现,如果你UPDATE行,它可能会移动到表格中的不同位置。

如果您遇到假定表有订单而他们没有订单的情况,您可以作为恢复选项根据表中元组的磁盘排序添加行号:

select row_number() OVER (), *
from the_table
order by ctid

如果输出看起来正确,我建议您CREATE TABLE一个带有额外字段的新表,然后执行INSERT INTO ... SELECT以插入ctid排序的数据,然后{{1表,最后修复任何外键引用,使它们指向新表。

ALTER TABLE ... RENAME可以通过autovacuum,ctidUPDATE等进行更改,因此不是应该在应用程序中使用的内容。我在这里使用它只是因为它听起来你没有任何真正的订购或标识符密钥。

如果您需要根据磁盘上的顺序配对行(如上所述,这是不可靠且不安全的事情),您可以per this SQLFiddle尝试:

CLUSTER

从不在生产应用中依赖于此。如果您真的遇到困难,可以使用WITH rn_c1(rn, col) AS ( SELECT row_number() OVER (ORDER BY ctid), c1.col FROM c1 ), rn_c2(rn, col) AS ( SELECT row_number() OVER (ORDER BY ctid), c2.col FROM c2 ) SELECT rn_c1.col AS c1, rn_c2.col AS c2 FROM rn_c1 INNER JOIN rn_c2 ON (rn_c1.rn = rn_c2.rn); 来构建一个新表,当您从缺少必需密钥的数据库中恢复数据时,可以从这个表开始,但这就是它。

当使用缺少ctid的集合时,上面给出的相同方法可能适用于空窗口子句CREATE TABLE AS而不是(),例如来自函数的中间结果。然而,它更不安全,而且应该只是最后的手段。

(另见这个较新的相关答案:https://stackoverflow.com/a/17762282/398670