我有一个Oracle SQL查询,我希望返回某个列具有值的所有行,但如果没有任何行具有该列的值,那么我想返回其中一个空行。如何实现这一目标?
我的场景是一个学习路径,其中包含要完成的课程,但我想显示完成百分比。显然,如果没有完成课程,我想展示完成百分百的学习路径。
查询非常复杂,所以我宁愿在下面提供虚拟场景:
learning_paths
--------------
learning_path_id learning_path_name
1 Oracle Developer
2 Python Developer
courses
-------
course_id course_name
--------- -----------
1 Oracle SQL
2 Oracle PL/SQL
3 Python
4 Django
5 NLTK
learning_path_items
-------------------
learning_path_id course_id
---------------- ---------
1 1
1 2
2 3
2 4
2 5
learning_path_enrollments
-------------------------
employee_id learning_path_id
----------- ----------------
1 1
2 2
3 2
course_enrollments
------------------
employee_id course_id
----------- ---------
2 3
2 4
So my results should be:
employee_id learning_path_id course_id completion
----------- ---------------- --------- ----------
1 1 0 out of 2
2 2 3 2 out of 3
2 2 4 2 out of 3
3 2 0 out of 3
以下是实际查询,这是非常复杂的,仍在进行中。
select sub2.director,
sub2.cost_centre,
sub1.employee_number,
sub1.employee_name,
sub2.job_title,
sub1.course_name,
sub1.learning_path_name,
sub2.course_start_date,
sub2.course_end_date,
max(sub2.course_end_date) over (partition by sub1.employee_number, sub1.learning_path_name) max_course_end_date,
sub1.original_date_of_hire,
sub2.job_start_date promotion_date,
nvl(sub1.completion_target_days, 9999999999999999) completion_target_days,
sub1.completion_target_date,
sub1.no_of_completed_courses,
sub1.no_of_mandatory_courses,
round(least(sub1.no_of_completed_courses, sub1.no_of_mandatory_courses) / sub1.no_of_mandatory_courses * 100) completion_percentage
--round() time_percentage
from (
select papf.employee_number,
nvl(papf.known_as, papf.first_name) || ' ' || papf.last_name employee_name,
papf.original_date_of_hire,
oav.version_name course_name,
olpt.name learning_path_name,
ole.completion_target_date completion_target_date,
ole.no_of_completed_courses no_of_completed_courses,
ole.no_of_mandatory_courses no_of_mandatory_courses,
olp.duration completion_target_days,
papf.person_id,
oav.activity_version_id
from ota_lp_enrollments ole,
per_all_people_f papf,
ota_learning_paths olp,
ota_learning_paths_tl olpt,
ota_lp_sections ols,
ota_lp_sections_tl olst,
ota_learning_path_members olpm,
ota_activity_versions oav -- aka Courses
where papf.person_id = ole.person_id
and xxpay_bi_util.get_effective_date(ole.person_id, trunc(sysdate), 'all') between papf.effective_start_date
and papf.effective_end_date
and olp.learning_path_id = ole.learning_path_id
and olpt.learning_path_id = ole.learning_path_id
and olpt.language = userenv('LANG')
and ols.learning_path_id = ole.learning_path_id
and olst.learning_path_section_id = ols.learning_path_section_id
and olst.language = userenv('LANG')
and ols.learning_path_id = ole.learning_path_id
and olpm.learning_path_id = ole.learning_path_id
and olpm.learning_path_section_id = ols.learning_path_section_id
and oav.activity_version_id = olpm.activity_version_id
group by papf.employee_number,
nvl(papf.known_as, papf.first_name) || ' ' || papf.last_name,
papf.original_date_of_hire,
oav.version_name,
olpt.name,
olst.name,
ole.completion_target_date,
ole.no_of_completed_courses,
ole.no_of_mandatory_courses,
olp.duration,
papf.person_id,
oav.activity_version_id
) sub1,
(
select erm.*,
paaf.assignment_id,
paaf.person_id,
pj.name job_title,
haou.name cost_centre,
paafm.job_start_date,
xxpay_util.get_lookup_value(trunc(sysdate), 'TRUW_HR_DIRECTORS', hoi.org_information1) director -- 'Derek Kohler (Stores)'
from (
select odb.booking_id,
odb.delegate_person_id,
odb.date_booking_placed,
oe.course_start_date,
oe.course_end_date,
oe.course_start_time,
oe.course_end_time,
oe.enrolment_start_date,
oe.public_event_flag,
oe.title class_title,
oe.activity_version_id
from ota_delegate_bookings odb,
ota_events oe -- aka Classes
where oe.event_id = odb.event_id
) erm,
per_all_assignments_f paaf,
per_jobs pj,
hr_all_organization_units haou,
hr_organization_information hoi,
(
select paaf.person_id,
paaf.assignment_id,
paaf.job_id,
min(paaf.effective_start_date) job_start_date
from per_all_assignments_f paaf
where paaf.assignment_type in ('E', 'C')
and paaf.primary_flag = 'Y'
group by paaf.person_id,
paaf.assignment_id,
paaf.job_id
) paafm
where paaf.person_id (+) = erm.delegate_person_id
and erm.course_start_date between nvl(paaf.effective_start_date (+), to_date('01/01/1000', 'dd/mm/yyyy'))
and nvl(paaf.effective_end_date (+), to_date('31/12/4712', 'dd/mm/yyyy'))
and paafm.person_id (+) = paaf.person_id
and paafm.assignment_id (+) = paaf.assignment_id
and paafm.job_id (+) = paaf.job_id
and pj.job_id (+) = paaf.job_id
and haou.organization_id (+) = paaf.organization_id
and hoi.organization_id (+) = paaf.organization_id
and hoi.org_information_context (+) = 'TRU_ADD_ORG'
) sub2
where sub2.activity_version_id (+) = sub1.activity_version_id
and sub2.person_id (+) = sub1.person_id
and sub1.employee_number in ('2006591', '2005681', '2004118', '2004212')
order by 3, 1, 2, 4, 5, 7
答案 0 :(得分:1)
如果你有这样的表:
CREATE TABLE students (
student_id NUMBER PRIMARY KEY
);
CREATE TABLE courses (
course_id NUMBER PRIMARY KEY
);
CREATE TABLE student_courses (
student_id NUMBER REFERENCES Students( student_id ),
course_id NUMBER REFERENCES Courses( course_id ),
completed NUMBER(1,0),
score NUMBER(3,0),
CONSTRAINT student_courses_pk PRIMARY KEY ( student_id, course_id )
);
然后你可以这样做:
SELECT s.student_id,
COUNT( CASE c.completed WHEN 1 THEN 1 END ) / COUNT( c.course_id ) * 100 AS percent_enrolled_completed
FROM student s
LEFT OUTER JOIN
student_courses c
ON ( s.student_id = c.student_id )
GROUP BY s.student_id;
已编辑:回答更新的问题
Oracle 11g R2架构设置:
create table learning_path_items AS
SELECT 1 AS learning_path_id, 1 AS course_id FROM DUAL
UNION ALL SELECT 1, 2 FROM DUAL
UNION ALL SELECT 2, 3 FROM DUAL
UNION ALL SELECT 2, 4 FROM DUAL
UNION ALL SELECT 2, 5 FROM DUAL;
create table learning_path_enrollments AS
SELECT 1 AS employee_id, 1 AS learning_path_id FROM DUAL
UNION ALL SELECT 2, 2 FROM DUAL
UNION ALL SELECT 3, 2 FROM DUAL;
create table course_enrollments AS
SELECT 2 AS employee_id, 3 AS course_id FROM DUAL
UNION ALL SELECT 2, 4 FROM DUAL;
查询1 :
WITH num_completed AS (
SELECT e.learning_path_id,
e.employee_id,
c.course_id,
COUNT( c.course_id ) OVER ( PARTITION BY e.learning_path_id, e.employee_id ) AS num_completed
FROM learning_path_items i
INNER JOIN
learning_path_enrollments e
ON (e.learning_path_id = i.learning_path_id)
INNER JOIN
course_enrollments c
ON (i.course_id = c.course_id
AND e.employee_id = c.employee_id)
),
num_courses AS (
SELECT e.learning_path_id,
e.employee_id,
COUNT( 1 ) AS num_courses
FROM learning_path_items i
INNER JOIN
learning_path_enrollments e
ON (e.learning_path_id = i.learning_path_id)
GROUP BY
e.learning_path_id,
e.employee_id
)
SELECT c.learning_path_id,
c.employee_id,
x.course_id,
COALESCE( x.num_completed, 0 ) || ' out of ' || c.num_courses AS completion
FROM num_courses c
LEFT OUTER JOIN
num_completed x
ON ( c.employee_id = x.employee_id
AND c.learning_path_id = x.learning_path_id )
ORDER BY 1, 2, 3
<强> Results 强>:
| LEARNING_PATH_ID | EMPLOYEE_ID | COURSE_ID | COMPLETION |
|------------------|-------------|-----------|------------|
| 1 | 1 | (null) | 0 out of 2 |
| 2 | 2 | 3 | 2 out of 3 |
| 2 | 2 | 4 | 2 out of 3 |
| 2 | 3 | (null) | 0 out of 3 |
答案 1 :(得分:0)
从UNION ALL SELECT 0 FROM DUAL LIMIT 1中选择任何FROM; 如果您从第一个查询中获得结果,那么您将得到1行,其中包含您尝试获得的百分比 第二个选择返回一个值为0的行,但是存储在虚拟表DUAL的VARCHAR2(1)中,因此您将从第一个查询中获得结果(假设您只获得1个结果,否则它只显示第一行它返回) 如果第一个选择不返回任何行,则第二个选择将返回单个列单行结果,其中包含varchar2(1)&#39; 0&#39;
可选地
SELECT
something
FROM sometable
WHERE somefield = somevalue
UNION ALL
SELECT
0
WHERE NOT EXISTS (SELECT something FROM sometable WHERE somefield = somevalue)
除非你的第一个选择查询没有返回任何行,否则这将做同样的事情,我想这对你的问题来说是更好的答案,因为第一个查询可能会返回多行