3

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

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 字符串指定的所有测试的数据。

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

4

2 回答 2

4
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可以做同样的事情。

在我重新阅读并考虑了您的问题后,我明白这并不完全是您所要求的。此查询的目标更多是检查学生是否通过了每个科目(即,如果您第一次未通过考试,那么您重新参加考试并最终通过)。

于 2013-10-29T06:19:10.070 回答
1

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

或者,考虑您是否真的需要发送 csv,或者您是否可以发送多个参数。类似的东西Test in (param1,param2,....paramX)。如果您允许有限数量的参数,这是可行的。如果您发送数千个参数,它当然不会起作用,imo 有十几个正在推送它,但是如果您总是发送 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
于 2013-10-29T06:55:13.147 回答