我需要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
)
答案 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
)