oracle sql中的相关查询

时间:2016-03-23 16:40:17

标签: sql oracle

我需要Oracle Sql中相关子查询的帮助。 问题是,第二级深度子查询包含daily.day,因此此查询会导致错误。

DAILY - columns: daily_id, day, emp_details_id, worked_hour 

EMP_DETAILS - columns: emp_details_id, valid_from, valid_to, detail_type, detail_value

我想获取每行的detail_value,其中行的日期介于ed.valid_from和ed.valid_to之间。然后我想把这一天排成一行,其中ed.valid_from是最好的(最近的)。 因此,我想了解给定emp_details_id

的最新有效详细信息值

示例:(我只编写了所需的列)

DAILY

day = '2016-03-02', emp_details_id = 1
day = '2016-03-04', emp_details_id = 1

EMP_DETAILS

valid_from = '2016-01-01', valid_to = '2016-12-31', detail_value = 6, emp_details_id = 1
valid_from = '2016-03-02', valid_to = '2016-12-31', detail_value = 7, emp_details_id = 1
valid_from = '2016-03-03', valid_to = '2016-12-31', detail_value = 8, emp_details_id = 1
valid_from = '2016-03-01', valid_to = '2016-12-31', detail_value = 10, emp_details_id = 2

结果:

day = '2016-03-02', valid_from = '2016-03-02', valid_to = '2016-12-31', detail_value = 7, emp_details_id = 1
day = '2016-03-04', valid_from = '2016-03-03', valid_to = '2016-12-31', detail_value = 8, emp_details_id = 1

我的查询:

SELECT
   da.*,
   ed.detail_value
FROM
   DAILY da
   INNER JOIN EMP_DETAILS ed 
      ON(da.emp_details_id = ed.emp_details_id)
WHERE
   ed.detail_value =
   (SELECT worktime.detail_value
      FROM
      (SELECT 
          ed2.detail_value
       FROM
          EMP_DETAILS ed2
       WHERE
          ed2.valid_from <= da.day AND --error
          ed2.valid_to >= da.day AND --error
          ed2.emp_details_id = ed.emp_details_id --error
       ORDER BY ed2.valid_from DESC
      ) worktime

    WHERE 
       ROWNUM = 1
   )

3 个答案:

答案 0 :(得分:1)

您需要在子查询中查询DAILY。此外,您可以使用子查询中的MAX函数删除嵌套子查询,ORDER BY ... DESC和ROWNUM = 1,并使用FIRST or LAST聚合变体来获取与最新日期对应的DETAIL_VALUE:

SELECT d.*,
       ed.DETAIL_VALUE
FROM DAILY d
INNER JOIN EMP_DETAILS ed 
  ON ed.EMP_DETAILS_ID = d.EMP_DETAILS_ID
WHERE (d.EMP_DETAILS_ID, d.DAY, ed.DETAIL_VALUE) IN
          (SELECT d2.EMP_DETAILS_ID, d2.DAY,
                MAX(ed2.DETAIL_VALUE) KEEP (DENSE_RANK LAST ORDER BY ed2.VALID_FROM)
             FROM DAILY d2
             INNER JOIN EMP_DETAILS ed2
               ON ed2.EMP_DETAILS_ID = d2.EMP_DETAILS_ID
             WHERE d2.DAY BETWEEN ed2.VALID_FROM
                              AND ed2.VALID_TO
             GROUP BY d2.EMP_DETAILS_ID, d2.DAY);

DAY        EMP_DETAILS_ID DETAIL_VALUE
---------- -------------- ------------
2016-03-02              1            7
2016-03-04              1            8

在这个简化的示例中,子查询本身实际上可以找到所需的所有信息:

SELECT d2.EMP_DETAILS_ID, d2.DAY,
     MAX(ed2.DETAIL_VALUE) KEEP (DENSE_RANK LAST ORDER BY ed2.VALID_FROM)
    FROM DAILY d2
  INNER JOIN EMP_DETAILS ed2
    ON ed2.EMP_DETAILS_ID = d2.EMP_DETAILS_ID
  WHERE d2.DAY BETWEEN ed2.VALID_FROM
                   AND ed2.VALID_TO
  GROUP BY d2.EMP_DETAILS_ID, d2.DAY;

EMP_DETAILS_ID DAY        MAX(ED2.DETAIL_VALUE)KEEP(DENSE_RANKLAS
-------------- ---------- ---------------------------------------
             1 2016-03-02                                       7
             1 2016-03-04                                       8

你可以很简单地从DAILY获得其他字段;对于其他EMP_DETAILS,您需要使用更多MAX KEEP DENSE_RANK配方。如果它变得太混乱或复杂,那么使用它作为子查询并加入它,就像在第一个例子中一样,可能更清楚 - 但效率会降低,因为它必须两次击中两个表。

祝你好运。

答案 1 :(得分:1)

您可以使用分析查询按照ed.valid_from记录的最新daily日期对连接的行进行排名,从而避免自联接。基本查询类似于:

SELECT
   daily.*,
   ed.*,
   rank() over (partition by daily.emp_details_id, daily.day
     order by ed.valid_from DESC) rnk
FROM
   DAILY daily
INNER JOIN EMP_DETAILS ed 
ON daily.emp_details_id = ed.emp_details_id
AND ed.valid_from <= daily.day
AND ed.valid_to >= daily.day;

DAY        EMP_DETAILS_ID VALID_FROM VALID_TO   DETAIL_VALUE EMP_DETAILS_ID        RNK
---------- -------------- ---------- ---------- ------------ -------------- ----------
2016-03-02              1 2016-03-02 2016-12-31            7              1          1
2016-03-02              1 2016-01-01 2016-12-31            6              1          2
2016-03-04              1 2016-03-03 2016-12-31            8              1          1
2016-03-04              1 2016-03-02 2016-12-31            7              1          2
2016-03-04              1 2016-01-01 2016-12-31            6              1          3

日期最长的记录排名为1,因此您可以将其放在子查询中并过滤生成的rnk列:

SELECT
   emp_details_id, day, detail_value
FROM
   (
   SELECT
      daily.day,
      daily.emp_details_id,
      ed.detail_value,
      rank() over (partition by daily.emp_details_id, daily.day
        order by ed.valid_from DESC) rnk
   FROM
      DAILY daily
   INNER JOIN EMP_DETAILS ed 
   ON daily.emp_details_id = ed.emp_details_id
   AND ed.valid_from <= daily.day
   AND ed.valid_to >= daily.day
)
WHERE
   rnk = 1;

EMP_DETAILS_ID DAY        DETAIL_VALUE
-------------- ---------- ------------
             1 2016-03-02            7
             1 2016-03-04            8

从数据来看,你看起来不太可能有两个匹配的记录,但是如果你做了(如果7和8我们都在同一天生效)那么这将返回两行。您需要调整partition by子句以选择如何打破平局。 (您也可以使用dense_rank,row_number等,但同样适用 - 如果可以存在平局,则应指定如何打破它。)

答案 2 :(得分:0)

您的查询只需极少的更改:

SELECT
  da.*,
  ed.detail_value
FROM
  DAILY da 
inner join EMP_DETAILS ed ON da.emp_details_id = ed.emp_details_id  
where ed.detail_value =
  (SELECT detail_value
    FROM ( 
      SELECT *
      FROM EMP_DETAILS 
      ORDER BY valid_from DESC) ed2
    WHERE ROWNUM =1 and
    ed2.valid_from <= da.day AND --error
    ed2.valid_to >= da.day AND --error
    ed2.emp_details_id = ed.emp_details_id --error
  )
相关问题