我有一个包含一些字段和样本记录的数据库表,如下所示:
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
可以有多个记录对应于学生和考试的一个组合(多次通过和失败)
答案 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