SQL查询检查始终继续的列

时间:2015-01-11 11:12:34

标签: sql oracle

我有一张名为GRADE的表,其中包含GRADE_NAME,POIN_FROM,POIN_TO列。 GRADE表如下所示:

GRADE_NAME        POIN_MIN       POIN_MAX
A                      90           100
B                      75            89
C                      50            69
D                      30            49
E                      10            29
F                       0            10

现在,我想编写一个sql查询来检查列poin_min和poin_max中的所有值是否有效。 在这种情况下,如果poin_min和poin_max包含如上所述的值,则我的查询应返回1(false),因为每个值不是相互顺序的。

    等级F的
  • poin_max应为9.
  • B级的
  • poin_min应为70。

但是,如果值始终是一个序列,那么我的查询返回0(true)。

这就是我的尝试:

SELECT CASE WHEN SUM(CEK) = 100 THEN '0' ELSE '1' END AS RESULT
FROM (
    SELECT POIN_MAX - POIN_MIN AS CEK
    FROM GRADE
    ORDER BY GRADE_NAME ASC
)

如你所见,这不起作用。因为虽然 SUM(CEK)= 100 ,但该值不一定是序列。

我正在使用oracle,但如果有人知道如何用另一个dbms解决这个问题请分享,这将非常有帮助。

ps:此表的行是动态的。用户可以为grade_name添加一些ROW,如G,H等,但poin_min和poin_max总是0到100。

谢谢,

鲁巴马拉姆

2 个答案:

答案 0 :(得分:1)

一个选项是将每个范围扩展为各个值,您可以使用recursive subquery factoring(在11GR2或更高版本中);然后查看所有成绩的整体列表。例如,仅适用于F:

with r (grade_name, poin, poin_max) as (
  select grade_name, poin_min, poin_max
  from grade
  union all
  select r.grade_name, r.poin + 1, r.poin_max
  from r
  where r.poin < r.poin_max
)
select grade_name, poin
from r
where grade_name = 'F';

GRADE_NAME       POIN
---------- ----------
F                   0 
F                   1 
F                   2 
F                   3 
F                   4 
F                   5 
F                   6 
F                   7 
F                   8 
F                   9 
F                  10 

要查看是否存在任何差异,您可以比较各种计数:

with r (grade_name, poin, poin_max) as (
  select grade_name, poin_min, poin_max
  from grade
  union all
  select r.grade_name, r.poin + 1, r.poin_max
  from r
  where r.poin < r.poin_max
)
select count(poin), count(distinct poin), min(poin), max(poin)
from r
having count(poin) != 101
or count(distinct poin) != 101
or min(poin) != 0
or max(poin) != 100;

COUNT(POIN) COUNT(DISTINCTPOIN)  MIN(POIN)  MAX(POIN)
----------- ------------------- ---------- ----------
         97                  96          0        100 

或者,如果您只想要原始的0/1结果:

with r (grade_name, poin, poin_max) as (
  select grade_name, poin_min, poin_max
  from grade
  union all
  select r.grade_name, r.poin + 1, r.poin_max
  from r
  where r.poin < r.poin_max
)
select case when count(poin) != 101 or count(distinct poin) != 101
  or min(poin) != 0 or max(poin) != 100 then 1 else 0 end as result
from r;

    RESULT
----------
         1 

您还可以更加雄心勃勃并报告实际问题值:

with r (grade_name, poin, poin_max) as (
  select grade_name, poin_min, poin_max
  from grade
  union all
  select r.grade_name, r.poin + 1, r.poin_max
  from r
  where r.poin < r.poin_max
),
n as (
  select level - 1 as poin from dual
  connect by level <= 101
)
select coalesce(n.poin, r.poin),
  count(r.poin), min(r.grade_name), max(r.grade_name)
from n
full outer join r on r.poin = n.poin
group by coalesce(n.poin, r.poin)
having count(r.poin) != 1
or count(n.poin) != 1
order by coalesce(n.poin, r.poin);

COALESCE(N.POIN,R.POIN) COUNT(R.POIN) MIN(R.GRADE_NAME) MAX(R.GRADE_NAME)
----------------------- ------------- ----------------- -----------------
                     10             2 E                 F                 
                     70             0                                     
                     71             0                                     
                     72             0                                     
                     73             0                                     
                     74             0                                     

此处n是另一个CTE,它只生成所有预期的有效值,并且递归CTE的外部联接可以让您看到任何不应该丢失,重复或存在的CTE。例如,如果你改为A 90-101的范围,它也会报告:

                    101             1 A                 A                 

SQL Fiddle包括101的范围;虽然你希望已经对有效值的范围有一个约束,所以你不能在第一时间创建这样的记录。 another Fiddle其中值是连续的。

答案 1 :(得分:1)

这是一个关于窗口函数的想法:使用上一行和下一行的poin_max创建一个CTE(按poin_min排序),然后运行检查:

with my_grade as (
select
  lead(poin_max) over (order by poin_min) next_max
, lag(poin_max) over (order by poin_min) prev_max
, poin_min
, poin_max
from grade
)
select
  case when prev_max is null and poin_min <> 0 then 1 else 0 end min_error
, case when next_max is null and poin_max <> 100 then 1 else 0 end max_error
, case when prev_max is not null and poin_min <> prev_max + 1 then 1 else 0 end step_error
, poin_min, poin_max
, prev_max, next_max
from my_grade;

如果最小值不为零,则min_error为1;如果最大值不为100,则max_error为1;如果前一个最大值为n,则最后一列为1恰好低于当前最小值。