SQL - 让所有科目都通过的学生

时间:2013-10-29 06:11:44

标签: sql oracle11g

我有一个包含一些字段和样本记录的数据库表,如下所示:

Name    Test    Result  Other_fields  
A      English    P         x  
B      Maths      F         x  
C      English    P         x  
B      English    P         x  
A      Maths      F         x  
D      English    P         x  
C      Biology    F         x  
A      Biology    P         x  
B      Biology    F         x  

现在实际的表包含了更多种类的测试 我想要实现的是找到学生的计数/列表(以及其他领域中对学生来说唯一的相应值...说出他的年龄,性别等),他们通过了逗号中指定的所有测试 - 分隔字符串。显然,该字符串在运行时插入到查询中。

现在,如果是固定数量的测试说2次测试或3次测试,那么我可以为它编写一个查询 但真正的问题是CSV字符串中的测试次数并不固定 有什么方法可以实现这个目标吗?

PS:如果计划建议一个涉及使用SQL IN的答案,我可以用一些唯一的数字(用作测试ID)替换测试名称。

修改

显然,我没有说清楚自己。该表将包含大量测试的记录(例如test1,test2,.... test100)。 CSV字符串将包含一些测试(例如2或3或4但绝对超过1)。我想要学生通过CSV字符串指定的所有测试的数据。

编辑2
可以有多个记录对应于学生和考试的一个组合(多次通过和失败)

2 个答案:

答案 0 :(得分:3)

SELECT DISTINCT Name 
  FROM Table t
 WHERE NOT EXISTS(SELECT 1
                    FROM Table 
                   WHERE     t.Name = Name 
                         AND Result = 'F'
                         AND ',' || CsvOfTests || ',' LIKE '%,' || Test || ',%'
                 )

我建议使用一些独特的值,例如自动递增的id而不是此处的名称。如果要在结果集中包含其他字段,可以将它们包含在SELECT和GROUP BY字段列表中,或者使用连接。

CsvOfTests是您感兴趣的测试列表。我在CsvOfTests和Test的开头和结尾添加了逗号,以防一个测试的名称是其他测试名称的子字符串。 但是,我不得不说,如果你将测试放入临时表而不是csv字符串,我觉得查询会更简单,更简单。

如果您的输入字符串将测试分隔的不仅仅是逗号(就像逗号之后的空格一样,您还需要包含这些空格)。如果它比那更复杂,那么我想我会尝试REGEXP_LIKE或编写我自己的函数。

根据请求计算传递主题的查询

SELECT DISTINCT Name 
  FROM Table t
 WHERE LENGTH(CsvOfTests) - LENGTH(REPLACE(CsvOfTests, ',')) + 1 = (
                  SELECT COUNT(1)
                    FROM Table 
                   WHERE     t.Name = Name 
                         AND Result = 'P'
                         AND ',' || CsvOfTests || ',' LIKE '%,' || Test || ',%'
                 )

此处LENGTH(CsvOfTests) - LENGTH(REPLACE(CsvOfTests, ',')) + 1计算列表中的测试次数。然后它只计算学生已经通过了多少次测试。显然,在{11g第1版中也有REGEXP_COUNT也可以这样做。

在我重读并思考了你的问题之后,我明白这并不是你所要求的。这个查询的目标更多的是检查学生是否已通过每个科目(即如果你第一次没有通过考试,那么你重新考试并最终通过)。

答案 1 :(得分:1)

您想要查找如何将csv转换为表格。我从来没有为Oracle做过这个,但它应该很容易做到。

或者,考虑您是否确实需要发送csv,还是可以发送多个参数。像Test in (param1,param2,....paramX)这样的东西。如果您允许有限数量的参数,这是可行的。如果你发送了数以千计的参数,它当然不会起作用,即使你正在推送它,但是如果你总是像3-5那样发送它,它就可以正常工作。

编辑:这是sqlfiddle,基本上显示了我的意思以及如何操作。它分割一个字符串,连接字符串,计算匹配测试的数量,并列出学生通过所有传入测试的学生/测试信息。我的原始示例在1 = 1上有一个连接,因为我在列名方面遇到了麻烦,我想显示连接正在发生。

With t as (
Select 'English, Biology' as Tests from dual
),
Spl as(
Select rtrim(ltrim(REGEXP_SUBSTR (Tests, '[^,]+', 1, level))) As Tst,
 regexp_count(Tests,'[,]')+1 NumberOfTest
from t
connect by level<= regexp_count(Tests,'[,]')+1
)
, StudentPassing as (
Select count(TestName) over (partition by StudentName) TestPassed, 
    s.*, NumberOfTest
from StudentTest s 
  inner join Spl 
    on Tst=TestName
where TestResult='P'
)
select *
from StudentPassing
where TestPassed=NumberOfTest