【问题标题】:Oracle joins ( left outer, right, etc. :S )Oracle 连接(左外、右等:S)
【发布时间】:2010-09-16 03:59:36
【问题描述】:

我知道 stackoverflow 会帮助我,除了知道什么是“最喜欢的编程卡通”:P

这是被接受的答案: Bill Karwin

感谢大家的帮助(我想给大家投票)

我的查询结果是这样的(这是真实的)

SELECT 
    accepted.folio,
    COALESCE( inprog.activityin, accepted.activityin ) as activityin,
    inprog.participantin,
    accepted.completiondate 
FROM performance accepted
    LEFT OUTER JOIN performance inprog 
        ON( accepted.folio = inprog.folio 
            AND inprog.ACTIVITYIN 
            IN ( 4, 435 )                    -- both are ids for inprogress
            AND inprog.PARTICIPANTIN != 1  ) -- Ignore the "bot" participant
    LEFT OUTER JOIN performance closed
        ON( accepted.folio = closed.folio 
            AND closed.ACTIVITYIN IN ( 10,436, 4, 430  ) )  -- all these are closed or cancelled
WHERE accepted.ACTIVITYIN IN ( 3, 429 )      --- both are id for new 
AND accepted.folio IS NOT NULL
AND closed.folio IS NULL;

现在我只需加入其他表格即可获得人类可读的报告。


原帖

你好。

我挣扎了大约 6 个小时。现在有一个数据库查询(我的长期克星)

我有一个包含一些字段的数据表,例如:

table performance( 
     identifier varchar, 
     activity    number, 
     participant number, 
     closedate   date, 
)

用于跟踪ticket的历史记录

标识符:是一个客户 ID,例如 (NAF0000001)

activity:是工单所在位置的一个 fk(新、in_progress、rejected、close 等)

participant:代表当时参加票证的人

关闭日期:是该活动完成的日期。

编辑:我应该说“完成日期”而不是关闭日期。这是活动完成的日期,在关闭工单时不是必需的。

例如,典型的历史可能是这样的:

标识符|活动|参与者|关闭 ------------------------------------------ NA00000001| 1| 1|2008/10/08 15:00| ------------------------------------------ NA00000001| 2| 2|2008/10/08 15:20| ------------------------------------------ NA00000001| 3| 2|2008/10/08 15:40| ------------------------------------------ NA00000001| 4| 4|2008/10/08 17:05| ------------------------------------------

参与者 1=jonh, 2=scott, 3=mike, 4=rob

和活动 1=新,2=进行中,3=等待批准,4=关闭

等等。以及其他数十条无关信息。

我的问题如下。

我已经设法创建了一个查询,我可以在其中知道票证何时打开和关闭

是这样的:

 select 
     a.identifier,
     a.participant,
     a.closedate as start,
     b.closedate as finish      
from 
    performance a,
    performance b
where
    a.activity = 1 -- new
    and b.activity = 4 -- closed
    and a.identifier = b.identifier

但我不知道哪些票是没有关闭的,以及谁在参加。

到目前为止,我有这样的事情:

 select 
     a.identifier,
     a.participant,
     a.closedate as start
from 
    performance a        
where
    a.activity = 1 -- new
    and a.identifier not in ( select identifier from performance where activity = 4 ) --closed

这就是给我所有有开始(new = 1)但没有关闭(close = 4)的人

但这里最大的问题是它打印了打开票的参与者,但我需要参加它的参与者。所以我将“进行中”活动添加到查询中。

 select 
     a.identifier,
     a.participant,
     a.closedate as start
from 
    performance a,
    performance b        
where
    a.activity = 1 -- new        
    and a.identifier not in ( select identifier from performance where activity = 4 ) --closed
    and b.identifier = a.identifier
    and b.activity = 2  -- inprogress..

但并非“新”中的所有行都是“进行中”的,并且通过该查询我删除了所有行。

我需要显示所有“进行中”的参与者,如果票不是“进行中”,它将显示为空。

有点像

标识符|活动|参与者|关闭 ------------------------------------------ NA00000002| 1| |2008/10/08 15:00| ------------------------------------------ NA00000003| 1| |2008/10/08 15:20| ------------------------------------------ NA00000004| 1| |2008/10/08 15:40| ------------------------------------------ NA00000005| 2| 4|2008/10/08 15:40| ------------------------------------------ NA00000006| 2| 4|2008/10/08 15:40|

在这种情况下

NA002、NA003 和 NA004 处于“新”状态,因此没有显示参与者

虽然

NA005 和 NA006 正在“inprgress (act = 2)”并且他们正在被 rob (参与者 4) 参加

所以我记得有一个东西叫做左外连接或类似的东西,但我从来没有理解过。我想知道的是如何获取“正在进行”和“新”且未关闭的标识符。

也许休息一下会帮助我理清思路。如果有人知道该怎么做,我将不胜感激。

顺便说一句,我试过了:

 select 
     a.identifier,
     a.participant,
     a.closedate as start
from 
    performance a
    left outer join
    performance b  
    on      
    b.identifier = a.identifier
where
    a.activity = 1 -- new        
    and a.identifier not in ( select identifier from performance where activity = 4 ) --closed
    and b.activity = 2  -- inprogress..

但是给我的结果和以前一样(只删除“新”记录)

【问题讨论】:

    标签: sql oracle join left-join


    【解决方案1】:

    这个怎么样:

    SELECT * FROM (
      SELECT identifier,
             MAX(activity) activity,
             MAX(participant) KEEP (DENSE_RANK LAST ORDER BY activity)
        FROM performance
        GROUP BY identifier
    )
    WHERE activity in (1,2)
    

    内部查询给出了每张票及其对应参与者的最新活动。外部查询将其过滤到活动为“新”或“进行中”的活动。

    我喜欢 DENSE_RANK 函数。

    【讨论】:

    • 在历史记录中,关闭工单也具有新的“进行中”状态。因此,使用 where 活动 1,2 也会带来一张状态为 4 的票。:( 活动有奇怪的 id,不一定是连续的,不过为了清楚起见,我使用 1,2,3,4。谢谢。
    • 在有限的评论空间中很难解释。我建议您查看download.oracle.com/docs/cd/B19306_01/server.102/b14200/… 的 Oracle 文档,如果您需要更多解释,请发布一个新问题。
    【解决方案2】:

    只是一个快速的想法,其他人可能会在此基础上进行构建(未经测试,但我希望这个想法能够被采纳):

    首先,选择所有尚未结束的活动(由其他人发布):

    select id
    from performance p1 where identifier not exists
      (select * from performance p2 where activity=4 and p1.id=p2.id)
    

    然后,您可以通过在 select 子句中添加子查询来添加参加活动的人员:

    select id,
     (select participant 
      from performance p3 
      where p3.activity=3 and p1.id=p2.id)
    from performance p1 where identifier not exists
      (select * from performance p2 where activity=4 and p1.id=p2.id)
    

    如果此 id 没有活动 3 记录,则子查询返回 null,这正是我们所需要的。

    希望这会有所帮助 - 如果需要,请扩展。

    【讨论】:

      【解决方案3】:

      也许您可以使用这种查询作为起点。

      select x.identifier, 
             max(x.p_1) as new_participant, max(x.c_1) as new_date,
             max(x.p_2) as inprogress_participant, max(x.c_2) as inprogress_date,
             max(x.p_3) as approval_participant, max(x.c_3) as approval_date,
             max(x.p_4) as closing_participant, max(x.c_4) as closing_date
        from (
              select a.identifier, 
                     decode (activity, 1, participant, null) as p_1,  decode (activity, 1, closedate, null) as c_1,
                     decode (activity, 2, participant, null) as p_2,  decode (activity, 2, closedate, null) as c_2,
                     decode (activity, 3, participant, null) as p_3,  decode (activity, 3, closedate, null) as c_3,
                     decode (activity, 4, participant, null) as p_4,  decode (activity, 4, closedate, null) as c_4
                from performance a
              ) x
       group by x.identifier
      

      这个想法是将您的表格从行序列化到字段,并基于它创建一个视图。 您可以基于此视图创建报告。

      问候,

      【讨论】:

        【解决方案4】:

        我建议您想要的是最早的记录(大概,但不一定是活动 = 1 的记录)和最近的记录(无论活动编号如何)。如果最近记录的活动为 4,则该工单已关闭。否则,参与者是票的当前持有者。如果可以重新打开票证,仅匹配 activity = 4 会引入潜在的错误。

        实际上,根据您的示例,您甚至可能不需要最早的记录。以下怎么样:

        SELECT
                identifier,
                activity,
                participant,
                closedate
            FROM
                performance a
            WHERE
                (a.identifier, a.closedate) in
                    (select b.identifier, max(b.closedate)
                        from performance b
                        group by b.identifier
                    )
        ;
        

        【讨论】:

        • 就我而言,有大量的中间活动(好几十个),而 close 绝对是最后一个。使用这个查询也会带来无趣的状态,比如关闭和其他一些(取消、拒绝)谢谢。
        • 不赘述这种方法,但您可以进一步限定 where 子句以仅选择您感兴趣的活动,例如:“AND 活动 IN (1,2,4)”
        【解决方案5】:

        试试这样的(我还没有测试过):

        SELECT p_new.identifier, COALESCE(p_inprog.activity, p_new.activity) AS activity,
          p_inprog.participant, COALESCE(p_inprog.closedate, p_new.closedate) AS closedate
        FROM performance p_new
          LEFT OUTER JOIN performance p_inprog 
            ON (p_new.identifier = p_inprog.identifier AND p_inprog.activity = 2)
          LEFT OUTER JOIN performance p_closed 
            ON (p_new.identifier = p_closed.identifier AND p_closed.activity = 4)
        WHERE p_new.activity = 1
          AND p_closed.identifier IS NULL;
        

        我认为人们认为外部连接比实际更难。例如:

        A LEFT OUTER JOIN B ON (...condition...)
        

        这将返回 A 中的所有行,无论 B 中是否有任何匹配的行。如果 B 中没有匹配的行,则在 A 的该行的结果集中将所有列 B.* 视为 NULL。连接条件可以是 B 中的行必须满足的表达式,否则它不包含在连接中。因此,A 中的更多行将是单独的。

        【讨论】:

        • COALESCE 是一个 SQL 函数,它返回其第一个非 NULL 参数。在上面的示例中,如果 p_inprog 中的行与外部连接条件不匹配,则为 NULL,因此结果中的字段“默认”为 p_new 中的值。
        • 哇!!!。我不相信 oj 更难,它们对我来说更难我盯着我面前的 SQL,我就是不明白(好吧,每次都更清楚)但我不会来这个解决方案我自己。最后一个问题,为什么需要 p_closed.identifier 为空?感谢您的帮助!
        • 当p_closed.identifier为null时,表示最后一个join中没有匹配的行。这意味着不存在具有该标识符值且活动 = 4 的行。因此,票尚未关闭。
        【解决方案6】:

        我认为应该这样做。

        第一部分获取所有新的、未关闭且未进行中的记录。第二部分获取所有进行中的记录。然后我们将它们连接在一起,我们还可以通过在此查询周围包装一个“SELECT * FROM”来按标识符排序。

        select 
          a.identifier,
          a.participant,
          a.closedate as start
        from 
          performance a
        where
          a.activity = 1
          and not exists ( select identifier 
                           from performance b 
                           where b.activity = 4 
                           and b.identifier = a.identifier) 
          and not exists ( select identifier 
                           from performance c 
                           where c.activity = 2 
                           and c.identifier = a.identifier) 
        UNION ALL
        select 
          a.identifier,
          a.participant,
          a.closedate as start
        from 
          performance a
        where
          a.activity = 2
          and not exists ( select identifier 
                           from performance b 
                           where b.activity = 4 
                           and b.identifier = a.identifier); 
        

        【讨论】:

        • Uuuhhg .. 几乎。事实证明,所有 2 中的人都已经在 1 中。因为这是历史,而不是状态。但是......根据你的建议,我已经很接近了。我会发布生成的怪物...... :)
        • 好消息!我很高兴我终于在这里帮助了某人:D
        • ;) Abarax,你的帖子肯定会改变我对我想要的东西的看法,非常感谢。不幸的是,当我只需要一个时,它会复制记录( N001 1 和 N002 2 )。我尝试了下面 Bill Karwin 发布的解决方案,效果很好。无论如何感谢您 +1。
        【解决方案7】:

        哪些票未关闭:

        select identifier as closed_identifier 
          from performance where identifier not exists
          (select identifier from performance where activity=4)
        

        正在参加的门票:

        select identifier as inprogress_identifier, participant performance 
          from performance where activity=2
        

        未关闭的门票,其中的参与者正在参加:

        select * from 
          (select identifier as notclosed_identifier 
            from performance where identifier not exists
            (select identifier from performance where activity=4)) closed 
        left join 
          (select identifier as inprogress_identifier, participant performance 
            from performance where activity=2) attended 
        on notclosed_identifier=inprogress_identifier
        

        【讨论】:

          【解决方案8】:

          首先,如果您可以让客户同时打开多张票,您可能会遇到设计问题。理想情况下你应该有一个ticket_id,然后你可以使用ticket_id而不是标识符来执行Andy的查询。

          【讨论】:

          • 好吧,标识符是“ticket_id”,这不是使用ticket_id的查询,而是按周、天、小时等显示生产力的报告。设计来自第3方产品.你有什么想法我怎么能做到这一点?
          【解决方案9】:

          通常更好的编写方法是使用 EXISTS。第一个是:

          select * from performance p1
          where not exists 
              ( select * from performance p2 
                where p2.identifier = p1.identifier and p2.activity = 4 )
          

          通过这种方式,您可以对 performance.identifier 进行键控查找,而不必在 (select identifier from performance where activity=4) 中构建大量标识符。

          【讨论】:

          • 如果处于状态 2,如何显示“参与者”,如果处于其他状态,则什么都不显示?...我认为这与左侧外部的东西有关..我错了吗? ..
          猜你喜欢
          • 2010-09-26
          • 2014-02-24
          • 2016-10-02
          • 2015-01-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-13
          • 1970-01-01
          相关资源
          最近更新 更多