Oracle SQL至少返回一行

时间:2015-05-07 08:30:24

标签: sql oracle

我有一个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

2 个答案:

答案 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;

已编辑:回答更新的问题

SQL Fiddle

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)

除非你的第一个选择查询没有返回任何行,否则这将做同样的事情,我想这对你的问题来说是更好的答案,因为第一个查询可能会返回多行