Oracle SQL:嵌套子查询,外部相同,内部不同
在这种情况下,我有几个子查询,由UNION连接,每个子查询嵌套一个内部子查询。外部子查询彼此完全相同,而内部查询不同且唯一 对整个外部子查询的重用对于读取和进行更改来说是很麻烦的,如果可以一次性定义并重用它们,这将是非常有益的。因此,这是一个关于创建可重用SQL查询的问题,但是要使用作为参数传递的不同内部子查询 对于我的示例,我将提供一个简化的案例,它与我的实际代码有相同的问题 我们在项目中使用Oracle SQL 假设我们有一个学校或大学的数据库,表中有PERSON、STUDENT、GRADE和COURSE,所有这些都是通过FK关系连接起来的 我需要运行一个收集列表的查询,对每个条件计算一次人数:Oracle SQL:嵌套子查询,外部相同,内部不同,sql,oracle,subquery,union,Sql,Oracle,Subquery,Union,在这种情况下,我有几个子查询,由UNION连接,每个子查询嵌套一个内部子查询。外部子查询彼此完全相同,而内部查询不同且唯一 对整个外部子查询的重用对于读取和进行更改来说是很麻烦的,如果可以一次性定义并重用它们,这将是非常有益的。因此,这是一个关于创建可重用SQL查询的问题,但是要使用作为参数传递的不同内部子查询 对于我的示例,我将提供一个简化的案例,它与我的实际代码有相同的问题 我们在项目中使用Oracle SQL 假设我们有一个学校或大学的数据库,表中有PERSON、STUDENT、GRADE
- 姓氏以字母“E”开头的学生人数
- 20岁以上学生人数
- 女性人数(包括但不限于学生)
- 以B级或更高成绩通过“中级挪威语”课程的学生人数
| Description | Number_of_students
1 | Last names beginning with letter "E" | 32
2 | Older than 20 years | 154
3 | All female persons | 356
4 | Passed "Intermediate Norwegian" with grade >= B | 12
下面是一个应该满足我需要的查询。
它由多个子查询组成,这些子查询通过UNION连接,并且都有自己独特的内部查询
代码远非辉煌,但这与重点无关。真正的问题是大幅提高可读性。外部子查询具有相同的可重用结构,但内部子查询不同
SELECT * FROM
-- 1st entry: Number of students on the last name 'E'
(SELECT 'Last names beginning with letter "E"' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
SELECT DISTINCT p1.ID FROM PERSON p1, STUDENT s1
WHERE p1.ID = s1.PERSON_ID AND p1.LASTNAME LIKE 'E%'
)
)
UNION
-- 2nd entry: Number of students older than 20 years
(SELECT 'Older than 20 years' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
SELECT DISTINCT p2.ID FROM PERSON p2, STUDENT s2
WHERE p2.ID = s2.PERSON_ID AND p2.AGE > 20
)
)
UNION
-- 3rd entry: Number of female persons, including but not limited to students
(SELECT 'All female persons' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
SELECT DISTINCT p3.ID FROM PERSON p3 WHERE p3.GENDER = 'Female'
)
)
UNION
-- 4th entry: Students who passed the course "Intermediate Norwegian" with grade B or higher
(SELECT 'Passed "Intermediate Norwegian" with grade >= B' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
SELECT DISTINCT p4.ID FROM PERSON p4, STUDENT s4, GRADE g4 AND COURSE c4
WHERE p4.ID = s4.PERSON_ID
AND s4.ID = g4.STUDENT_ID
AND g4.COURSE_ID = c4.ID
AND (g4.GRADE = 'A' OR g4.GRADE = 'B')
AND c4.COURSE_NAME = 'Intermediate Norwegian'
)
)
就像我说的,代码一点也不精彩。如果你们中的一些人对刚读到的东西感到畏缩,我不会感到惊讶
例如,整个第四个查询可以很容易地替换为一个查询,其中您将整个内部查询替换为g.GRADE='a'或'B'和c.COURSE_NAME='Intermediate norgian'
但正如我所说,这不是重点。
每个外部子查询都具有相同的结构:
(SELECT 'Passed "Intermediate Norwegian" with grade >= B' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
-- Inner Sub-query here
)
而每个子查询都有一个彼此不同的内部查询。像第一个和第三个:
SELECT DISTINCT p1.ID FROM PERSON p1, STUDENT s1 WHERE p1.ID = s1.PERSON_ID AND p1.LASTNAME LIKE 'E%'
及
我需要什么:
| Description | Number_of_students
1 | Last names beginning with letter "E" | 32
2 | Older than 20 years | 154
3 | All female persons | 356
4 | Passed "Intermediate Norwegian" with grade >= B | 12
我正在使用的实际代码要复杂得多,但与上面的示例中所示的问题相同
- 结果必须是一个包含多个数字的列表,每个数字按照各自不同的标准进行分类(最好在第一列中描述)
- 它由几个子查询组成,由UNION连接
- 这些子查询中的每一个子查询都是完全相同的,但内部子查询是完全唯一的,并且与其他子查询不同
- 由此产生的代码是一个巨大的野兽,但理论上,如果外部代码只编写了一次,并与作为参数传递的不同内部代码一起重用,则可以使其更具可读性
WITH outer_sub_query AS (
SELECT 'DESCRIPTION HERE' AS Description, count(*) AS Number_of_students FROM
FROM PERSON p, STUDENT s, GRADE g, COURSE c
WHERE p.ID = s.PERSON_ID
AND s.ID = g.STUDENT_ID
AND g.COURSE_ID = c.ID
-- ... other complex code here
AND p.ID IN(
-- INSERT INNER SUB-QUERY HERE
)
)
SELECT * FROM (
outer_sub_query -- Last Names beginning with letter 'E'
UNION
outer_sub_query -- Age > 20
UNION
outer_sub_query -- All female
UNION
outer_sub_query -- Passed that course with grade >= B
)
不幸的是,我的需要还没有得到满足。我仍然需要传递内部子查询以及描述。类似于此:
SELECT * FROM (
outer_sub_query(
'Last names beginning with letter "E",'
SELECT DISTINCT p1.ID FROM PERSON p1, STUDENT s1
WHERE p1.ID = s1.PERSON_ID AND p1.LASTNAME LIKE 'E%'
)
UNION
outer_sub_query(
'Older than 20 years.'
SELECT DISTINCT p2.ID FROM PERSON p2, STUDENT s2
WHERE p2.ID = s2.PERSON_ID AND p2.AGE > 20
)
UNION
outer_sub_query(
'All female persons'
SELECT DISTINCT p3.ID FROM PERSON p3 WHERE p3.GENDER = 'Female'
)
UNION
outer_sub_query(
'Passed "Intermediate Norwegian" with grade >= B'
SELECT DISTINCT p4.ID FROM PERSON p4, STUDENT s4, GRADE g4 AND COURSE c4
WHERE p4.ID = s4.PERSON_ID
AND s4.ID = g4.STUDENT_ID
AND g4.COURSE_ID = c4.ID
AND (g4.GRADE = 'A' OR g4.GRADE = 'B')
AND c4.COURSE_NAME = 'Intermediate Norwegian'
)
)
问题:
| Description | Number_of_students
1 | Last names beginning with letter "E" | 32
2 | Older than 20 years | 154
3 | All female persons | 356
4 | Passed "Intermediate Norwegian" with grade >= B | 12
现在,我们很容易想到定义函数。但它仍然给我带来了一些问题:
- 乍一看,WITH子句似乎不接受可以传递的参数。SQL或Oracle SQL中是否有其他预先存在的子句或函数处理此问题
- 是否可以将内部子查询从外部子查询中提取出来,并且仍然获得相同的结果?(请记住:外部子查询本身没有更改)
- 如果我要定义一个处理这个问题的函数,是否可以像上面那样传递纯SQL代码
- 我还缺少其他智能解决方案吗
WITH student_grades AS (
SELECT
p.id AS id,
p.lastname AS lastname,
p.age AS age,
p.gender AS gender,
c.course_name AS course_name,
g.grade AS grade
FROM
-- You really, really should use ANSI JOIN syntax:
PERSON p
JOIN STUDENT s ON p.ID = s.PERSON_ID
JOIN GRADE g ON s.ID = g.STUDENT_ID
JOIN COURSE c ON g.COURSE_ID = c.ID
-- WHERE ... other complex code here
)
然后,您可以继续查询
-- 1st entry: Number of students on the last name 'E'
SELECT
'Last names beginning with letter "E"' AS Description,
count(distinct sg1.id) AS Number_of_students
FROM student_grades sg1
WHERE sg1.lastname LIKE 'E%'
UNION
-- 2nd entry: Number of students older than 20 years
SELECT
'Older than 20 years' AS Description,
count(distinct sg2.id) AS Number_of_students
FROM student_grades sg2
WHERE sg2.AGE > 20
UNION
-- 3rd entry: Number of female persons, including but not limited to students
-- NOTE: THIS ONE MATCHES YOUR ORIGINAL, WHICH IS INCORRECT
SELECT
'All female persons' AS Description,
count(distinct sg3.id) AS Number_of_students
FROM student_grades sg3
WHERE sg3.GENDER = 'Female'
UNION
-- 4th entry: Students who passed the course "Intermediate Norwegian" with grade B or higher
SELECT
'Passed "Intermediate Norwegian" with grade >= B' AS Description,
count(distinct sg4.id) AS Number_of_students
FROM student_grades sg4
WHERE
sg4.COURSE_NAME = 'Intermediate Norwegian'
AND sg4.grade IN ('A', 'B')
这实际上是一个显著的进步。请特别注意,您不需要将条件(无论是否子查询)传递到CTE中;相反,您可以查询CTE(也可以将其连接到其他表等)。当然,部分原因是你的“内部”子查询是一种非常可怕的处理方式;相反,我使用了count(distinct sg.id)
,只要person.id
为非空,就可以实现与那些子查询相同的功能,我认为这是因为它是PK
但也要注意,即使是需要一个不同的计数(以及查询的第三部分的错误),也是因为首先尝试使用相同的公共中间结果来完成所有四个部分。您不需要加入课程或成绩信息来查询严格与个人特征相关的信息,只要学生与个人的关系为0,1:1,省略课程和成绩信息将免费为您提供一个明确的计数
至于第三部分,加入student
表将结果限制在学生身上,这是您不想要的。事实上,您没有在“内部”子查询中设置该限制是不相关的;您正在使用该子查询来筛选只包括最初是学生的人的结果。因此,*在这种情况下,您的方法无法产生您想要的结果**
也许是
select count(case when lastname like 'e%' then 1 end) as lastname_starts_with_e
,count(case when age > 20 then 1 end) as age_greater_than_20
,count(case when gender = 'Female' then 1 end) as is_female
from person
;