1

我在 Postgres 表中有一些数据,如下所示:

1 apple datetime1
2 orange datetime2
3 apple datetime3
4 orange datetime4
5 apple datetime5
6 orange datetime6
.

日期时间总是按升序排列,大多数时候苹果行首先插入,橙色排在第二位,但有一些我必须捕捉和消除的例外情况。

我实际上需要的是一个 Postgres 查询,它只配对苹果和橙子:

1 apple datetime1 2 orange datetime2
3 apple datetime3 4 orange datetime4
5 apple datetime5 6 orange datetime6

苹果不应该和其他苹果搭配,橙子也不应该和其他橙子搭配。

有几个条件:

1)在新生成的行中,苹果应该始终排在第一位,橙色排在第二位。

2) 始终将 apple 和 orange 行与最接近的日期时间配对,并忽略其他行。

例如,如果我的原始数据如下所示:

1 apple datetime1
2 apple datetime2
3 orange datetime3
4 orange datetime4

一对

2 apple datetime2 3 orange datetime3

并忽略行

1 apple datetime1
4 orange datetime4

任何想法如何在 Postgres 中做到这一点?

4

4 回答 4

2

日期时间总是按升序排列,大多数时候苹果行首先插入,橙色排在第二位,但有一些我必须捕捉和消除的例外情况。

如果我理解正确,您想查找是否有任何连续两行具有相同的水果,对吗?

如果是这样,您可以这样做:

WITH Q AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY datetime) R
    FROM YOUR_TABLE
)
SELECT *
FROM Q Q1 JOIN Q Q2 ON Q1.R = Q2.R - 1
WHERE Q1.fruit = Q2.fruit;

用简单的英语:将每一行与下一行连接起来(其中“下一个”的含义由datetime排序决定)并检查它们的水果是否匹配。您可以在这个SQL Fiddle中使用它。

顺便说一句,这不是一个好的数据库设计。您应该考虑重新设计,这样您上面描述的“无效”情况自然会被数据库结构本身阻止。

于 2012-06-04T16:07:32.503 回答
1

具有 CTE 和窗口功能的解决方案:

WITH x AS (
    SELECT *
          ,lead(tbl) OVER (ORDER BY id) AS y
    FROM tbl
    )
SELECT x.id,     x.fruit,   x.dt
     , (y).id, (y).fruit, (y).dt
FROM   x
WHERE  fruit = 'apple'
AND    (y).fruit = 'orange'
ORDER  BY x.id;

也可以作为子查询完成,但根据来自 @wildplasser 的请求。:)

我选择每个窗口函数的整个“下一个”行lead()。请注意使用括号访问复合(行)类型的列的语法y

于 2012-06-04T15:49:40.013 回答
0
select 
    t0.id, t0.fruit, t0.datetime, 
    t1.id, t1.fruit, t1.datetime
from t t0
inner join t t1 on 
    t0.fruit = 'apple' 
    and 
    t1.fruit = 'orange'
    and
    t0.datetime < t1.datetime
order by t1.datetime - t0.datetime
limit 1
于 2012-06-04T15:28:34.137 回答
0
SET search_path='lutser';

DROP TABLE fruits;
CREATE TABLE fruits
    ( id INTEGER NOT NULL
    , fruit varchar
    , zdate varchar
    );  
INSERT INTO fruits(id,fruit,zdate)
VALUES

(1, 'apple', 'datetime01')
, (2, 'orange', 'datetime02')
, (3, 'apple', 'datetime03')
, (4, 'orange', 'datetime04')
, (5, 'apple', 'datetime05')
, (6, 'orange', 'datetime06')
, (11, 'apple', 'datetime11')
, (12, 'apple', 'datetime12')
, (13, 'orange', 'datetime13')
, (14, 'orange', 'datetime14')
    ;

SELECT fa.id, fa.fruit, fa.zdate
    , fo.id, fo.fruit, fo.zdate
FROM fruits fa
JOIN fruits fo ON fa.zdate < fo.zdate
WHERE fa.fruit = 'apple' AND fo.fruit = 'orange'
AND NOT EXISTS (
    SELECT *
    FROM fruits nx
    WHERE nx.fruit = 'orange'
    AND nx.zdate > fa.zdate
    AND nx.zdate < fo.zdate
    )   
AND NOT EXISTS (
    SELECT *
    FROM fruits nx
    WHERE nx.fruit = 'apple'
    AND nx.zdate < fo.zdate
    AND nx.zdate > fa.zdate
    )   
    ;

结果:

DROP TABLE
CREATE TABLE
INSERT 0 10
 id | fruit |   zdate    | id | fruit  |   zdate    
----+-------+------------+----+--------+------------
  1 | apple | datetime01 |  2 | orange | datetime02
  3 | apple | datetime03 |  4 | orange | datetime04
  5 | apple | datetime05 |  6 | orange | datetime06
 12 | apple | datetime12 | 13 | orange | datetime13
(4 rows)
于 2012-06-04T15:33:34.227 回答