【问题标题】:Oracle SQL - select within a select (on the same table!)Oracle SQL - 在一个选择中选择(在同一张表上!)
【发布时间】:2011-04-03 06:44:29
【问题描述】:

我将尝试解释我想要快速实现的目标,因为我不知道如何解释它!

我们这里有一个表格,显示所有员工的所有工作经历,我想要当前职位的“Start_Date”(“Current_Flag”='Y')。除此之外,我想要之前帖子的“End_Date”(将按当前标志过滤,按结束日期排序,然后抓住最前面的那个)

无论如何,这是我的代码:

SELECT "Gc_Staff_Number",
       "Start_Date",
       (SELECT "End_Date"
        FROM   "Employment_History"
        WHERE  "Current_Flag" != 'Y'
               AND ROWNUM = 1
               AND "Employee_Number" = "Employment_History"."Employee_Number"
        ORDER  BY "End_Date" ASC)
FROM   "Employment_History"
WHERE  "Current_Flag" = 'Y'

任何关于如何使其工作的建议都会很棒,希望以上内容有点意义 - 老实说,目前的查询甚至无法正常工作,这真的很糟糕,嗯。

(编辑:哦!我写这个是为了查询一个现有的系统......由于某种原因,它在表和字段名称周围都有所有愚蠢的双引号,叹息!)

【问题讨论】:

  • 您是否尝试为“Employment_History”提供别名并在内部查询中使用该别名?
  • 您是否收到特定的错误消息?
  • 双引号的意思是当我们谈到名字时,大小写很重要。即,“Gc_Staff_Number”与“GC_Staff_Number”不同。在我的例子中我没有打扰他们 - 没有足够的耐心!

标签: sql oracle select join


【解决方案1】:

这正是分析可以发挥作用的那种场景。

鉴于此测试数据:

SQL> select * from employment_history
  2  order by Gc_Staff_Number
  3             , start_date
  4  /

GC_STAFF_NUMBER START_DAT END_DATE  C
--------------- --------- --------- -
           1111 16-OCT-09           Y
           2222 08-MAR-08 26-MAY-09 N
           2222 12-DEC-09           Y
           3333 18-MAR-07 08-MAR-08 N
           3333 01-JUL-09 21-MAR-09 N
           3333 30-JUL-10           Y

6 rows selected.

SQL> 

带有分析 LAG() 函数的内联视图提供了正确答案:

SQL> select Gc_Staff_Number
  2             , start_date
  3             , prev_end_date
  4  from   (
  5      select Gc_Staff_Number
  6             , start_date
  7             , lag (end_date) over (partition by Gc_Staff_Number
  8                                    order by start_date )
  9                  as prev_end_date
 10             , current_flag
 11      from employment_history
 12  )
 13  where current_flag = 'Y'
 14  /

GC_STAFF_NUMBER START_DAT PREV_END_
--------------- --------- ---------
           1111 16-OCT-09
           2222 12-DEC-09 26-MAY-09
           3333 30-JUL-10 21-MAR-09

SQL>

内联视图对于获得正确的结果至关重要。否则 CURRENT_FLAG 上的过滤器会删除之前的行。

【讨论】:

  • 绝对完美,非常感谢 - 以前甚至不知道延迟功能,但看起来非常方便。只是现在阅读它,以及分区位......干杯
  • 如果我在主选择中有 'GROUP BY' 条件。如何从内部选择中选择多个列?
  • @viuu - 你不能。规则非常明确:投影只能包含聚合值和 GROUP BY 子句中的列。如果这对您没有帮助,请发布问题,包括示例原始数据和您想要的输出。
【解决方案2】:

我对引号有点困惑,但是,下面应该对你有用:

SELECT "Gc_Staff_Number",
       "Start_Date", x.end_date
FROM   "Employment_History" eh,
(SELECT "End_Date"
        FROM   "Employment_History"
        WHERE  "Current_Flag" != 'Y'
               AND ROWNUM = 1
               AND "Employee_Number" = eh.Employee_Number
        ORDER  BY "End_Date" ASC) x
WHERE  "Current_Flag" = 'Y'

【讨论】:

  • 你 can't use ROWNUM 在 WHERE 子句中。
  • 那么您将获得第一行。更大的问题是 order by 子句适用于获取的结果集。因此,rownum 将返回匹配条件的第一条记录,然后它将执行排序依据。它不会生成一个完全有序的列表,然后选择第一行返回。
  • @Martin:你当然可以使用它。如果您使用过滤掉 ROWNUM=1 的条件,它只会产生不希望的结果。
  • 无论如何,在这种情况下,我将删除 ROWNUM 上的条件,删除 ORDER BY,并在内联视图中选择 MAX("End_Date"),原因是 Brett 指出的。 >
  • 您可以通过执行 (SELECT End_DATE FROM (SELECT End_Date FROM .. . . ORDER BY END_DATE ASC) WHERE ROWNUM = 1) 来使用 rownum。这会将 ROWNUM 应用于排序的结果。这应该会触发解释计划中的 STOP_KEY。但是 APC 的解析函数有更好的答案。
【解决方案3】:
SELECT eh."Gc_Staff_Number",
       eh."Start_Date",
       MAX(eh2."End_Date") AS "End_Date"
FROM   "Employment_History" eh
LEFT JOIN  "Employment_History" eh2
ON eh."Employee_Number" = eh2."Employee_Number" and eh2."Current_Flag" != 'Y'
WHERE  eh."Current_Flag" = 'Y' 
GROUP BY eh."Gc_Staff_Number",
       eh."Start_Date

【讨论】:

    【解决方案4】:
    SELECT "Gc_Staff_Number",
           "Start_Date",
           (SELECT "End_Date"
            FROM   "Employment_History"
            WHERE  "Current_Flag" != 'Y'
                   AND ROWNUM = 1
                   AND "Employee_Number" = "Employment_History"."Employee_Number"
            ORDER  BY "End_Date" ASC)
    FROM   "Employment_History"
    WHERE  "Current_Flag" = 'Y'
    

    仅供参考,在这种情况下,ROWNUM = 1 在 ORDER BY 之前被评估,因此内部查询将对总计(最多)一条记录进行排序。

    如果您真的在寻找给定员工的最早 end_date(其中 current_flag 'Y'),这就是您要寻找的吗?

    SELECT "Gc_Staff_Number",
           "Start_Date",
           eh.end_date
      FROM "Employment_History" eh
           LEFT OUTER JOIN -- in case the current record is the only record...
           (SELECT "Employee_Number"
                 , MIN("End_Date") as end_date
              FROM "Employment_History"
             WHERE "Current_Flag" != 'Y'
             GROUP BY "Employee_Number" 
           ) emp_end_date
              ON eh."Employee_Number" = emp_end_date."Employee_Number"
     WHERE eh."Current_Flag" = 'Y'
    

    【讨论】:

      【解决方案5】:

      这是我将LAG function 用于:

      SELECT eh.gc_staff_number,
             eh.start_date,
             LAG(eh.end_date) OVER (PARTITION BY eh.gc_staff_number
                                        ORDER BY eh.end_date) AS prev_end_date
        FROM EMPLOYMENT_HISTORY eh
       WHERE eh.current_flag = 'Y'
      

      如果您想查看前面的行,请使用LEAD function。

      兼容性:

      据我所知,9i+ 支持此功能,但我尚未确认是否像文档声明那样支持 8i。

      LEAD 和 LAG 终于是 ANSI,但目前只有 Oracle 和 PostgreSQL v8.4+ 支持它们。

      【讨论】:

      • 希望 SQL Server 能在某个时候得到这个。它是否需要按Employee_Number 或其他方式进行分区? lag 是否包含记录 WHERE eh.current_flag != 'Y'?
      • 但是您的 LAG 函数只会考虑 current_flag = 'Y' 的行,它不一定会返回同一员工的结束日期。
      【解决方案6】:

      基本上,你所要做的就是

      select ..., (select ... from ... where ...) as ..., ..., from ... where ...
      

      例如。 你可以在任何你想要的地方插入 (select ... from ... where) ,它将被相应的数据替换。

      我知道其他例子(即使他们每个人都非常棒:))对于新手来说有点复杂(比如我 :p),所以我希望这个“简单”的例子能帮助你们中的一些人:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-19
        • 2013-02-28
        • 2020-11-27
        • 2015-11-24
        • 1970-01-01
        相关资源
        最近更新 更多