1

有人可以给我一个关于棘手的 SQL 问题的线索吗?我搜索了类似的问题,最接近的方法似乎是这样,但这不是我的问题的解决方案: Oracle - grouping between pairs of records

我知道这可以通过 PL/SQL 过程来实现,但在这种情况下(Oracle)SQL 是必要的。我正在寻找一个 Oracle SQL SELECT 来隔离普通表的 ID = 4 和 5 的特殊记录。该表仅包含 2 列:ID 和 DATE 它包含如下数据:

ID  DATE          REMARK (this row is not part of table!)
---------------   ------------------------------------------
2   01-JAN-2013     
4   02-JAN-2013   A  
7   03-JAN-2013  
5   05-JAN-2013   A  
6   07-JAN-2013  
4   08-JAN-2013   B  
1   11-JAN-2013  
5   12-JAN-2013   B  

... more follows  

如果 ID 4 和 5 及时跟随,则它们属于同一组。因此,我标记为“A”的行属于一起,“B”也是如此。两个 As 和两个 B 属于一起,因为它们的日期是连续的。现在我想要得到的是一个返回 4 列的 SELECT,即一行中的两行 A,以及一行中的行 B。

所以输出应该是这样的:

ID4 DATE4        ID5 DATE5           Comment (no column, just comment)  
---------------------------------    ------------------------------------
4   02-JAN-2013  5   05-JAN-2013     First set of 4 and 5  
4   08-JAN-2013  5   12-JAN-2013     Second set of 4 and 5  

...more follows  

(当然 ID4 和 ID5 列已经过时,仅用于演示目的)

我希望我让自己可以理解?有人有想法吗?


更新:感谢您的想法和选择,很抱歉我的问题不够清楚。只应考虑成对的 4 和 5,并且只能按日期升序排列。对于上面的示例,Alex Pool 和 Florin Ghita 的解决方案很棒,谢谢!
4er 的日期必须小于或等于 5er 的日期。如果有一个 4er 或 5er 没有匹配的伙伴,它可以被忽略。

但这是另一个包含真实数据和陷阱的示例(解决方案在这里失败):按时间顺序遍历数据,有 2 行,连续 5 行。

4    16.03.2012 17:49:28  A
5    10.05.2012 09:38:56  A1     Either A1 is possible
5    12.06.2012 07:51:03  A2     or A2 whichever is easier to code
4    12.06.2012 08:47:52  B
5    02.08.2012 11:27:43  B
4    03.08.2012 13:24:54  C
5    03.08.2012 14:14:07  C
4    04.08.2012 15:00:00      should be ignored, as there is no following 5er

想要的输出:

4    16.03.2012 17:49:28    5    10.05.2012 09:38:56   (alternat.:  5  12.06.2012 07:51:03)
4    12.06.2012 08:47:52    5    02.08.2012 11:27:43  
4    03.08.2012 13:24:54    5    03.08.2012 14:14:07  

建议的 SELECTS 失败,因为他们不认为 4er 在时间上必须在 5er 之前。如果没有对应的伙伴,则忽略此行。我说得不够清楚,对不起。

非常感谢弗里德海姆

4

3 回答 3

2

像其他评论者一样,我不完全确定我是否遵循,但如果您只想查看 ID 4 和 5 并希望按日期顺序匹配它们,您可以执行以下操作:

with t as (
    select id, dt, row_number() over (partition by id order by dt) as rn
    from t42
    where id in (4, 5)
)
select t4.id as id4, t4.dt as date4, t5.id as id5, t5.dt as date5,
    case t4.rn when 1 then 'First' when 2 then 'Second' when 3 then 'Third' end
        || ' set of 4 and 5' as "Comment"
from t t4
join t t5 on t5.rn = t4.rn
where t4.id = 4
and t5.id = 5
order by t4.rn;

       ID4 DATE4            ID5 DATE5     Comment             
---------- --------- ---------- --------- ---------------------
         4 02-JAN-13          5 05-JAN-13 First set of 4 and 5  
         4 08-JAN-13          5 12-JAN-13 Second set of 4 and 5 

我现在不确定您是否真的想要返回/显示“评论”......可能不会,这会稍微简化它。


对于修改后的要求:

with t as (
    select id, dt, lead(dt) over (partition by id order by dt) as next_dt
    from t42
    where id in (4, 5)
)
select t4.id as id4, t4.dt as date4, t5.id as id5, min(t5.dt) as date5
from t t4
join t t5 on t5.dt > t4.dt and (t4.next_dt is null or t5.dt <= t4.next_dt)
where t4.id = 4
and t5.id = 5
group by t4.id, t4.dt, t5.id
order by t4.dt;

       ID4 DATE4                        ID5 DATE5               
---------- --------------------- ---------- ---------------------
         4 16.03.2012 17:49:28            5 10.05.2012 09:38:56   
         4 12.06.2012 08:47:52            5 02.08.2012 11:27:43   
         4 03.08.2012 13:24:54            5 03.08.2012 14:14:07   

CTE 用于LEAD查看每个 ID 的下一个日期,这仅在 ID 为 4 时才真正相关;如果最后没有没有匹配项的额外 ID 4,则可以为 null。然后,连接只查找两个 ID 4 日期之间(或最后一个 ID 4 日期之后)的 ID 5 记录。如果您想在第一个结果中使用备用(稍后)ID 5 日期,只需使用MAX而不是MIN. (我不是 100% 关于><=匹配;我试图解释你所说的,但如果它不太正确,你可能需要调整它)。


要解决似乎是 9i 的错误(根据 MOS,可能已在 9.2.0.3 或 9.2.0.6 中修复,但完全取决于您遇到的错误):

select t4.id as id4, t4.dt as date4, t5.id as id5, min(t5.dt) as date5
from (
    select id, dt, lead(dt) over (partition by id order by dt) as next_dt
    from t42
    where id = 4
) t4
join (select id, dt
    from t42
    where id = 5
) t5 on t5.dt > t4.dt and (t4.next_dt is null or t5.dt <= t4.next_dt)
group by t4.id, t4.dt, t5.id
order by t4.dt;

不幸的是,我没有足够旧的版本来测试它。您不必使用t5子选择,您可以直接将主表加入到t4,但我认为这更清楚一些。

于 2013-02-13T08:54:59.810 回答
1

你想要的很简单。只需根据 id 对您的记录进行排名。

with ranked_data

as (select 
       id, 
        date_col, 
       row_number() over (partition by id order by date_col) as rnk
   from your_table
   where id in (4, 5))

select t4.id as id4, t4.date_col as date4, t5.id as id5, t5.date_col as date5
from ranked_data t4
full outer join ranked_data t5 
  on (t4.rnk=t5.rnk and t4.id=4 and t5.id=5)
于 2013-02-13T08:53:31.863 回答
0

没有使用分析的自加入是可能的:

SELECT distinct 
         first_value(id) over (partition by rk order by dt), 
         min(dt) over (partition by rk),
         last_value(id) over (partition by rk order by dt rows between unbounded preceding and unbounded following) id5 ,
         max(dt) over (partition by rk) 
FROM (
SELECT id, dt, dense_rank() over (partition by id order by dt) rk 
FROM t
where  id in (4, 5)
)

这会找到对应于“第一个”日期的 id 值,因此如果 4 和 5 的日期顺序颠倒,那么您将 5 出现在“4”列中。目前尚不清楚这是否是您想要的。

于 2013-02-13T09:00:14.840 回答