Oracle SQL:根据另一个表中的值有条件地更新表

时间:2009-09-23 10:54:12

标签: sql oracle plsql

[以前的问题标题]

Oracle SQL:如果所有子表行在列中具有特定值,则更新父表列。仅更新所有科目中有100分的学生的RANK。如果学生在任何科目中的分数少于100分,则不应更新他的分数。

我有一个场景,我有一个父表和一个子表。子表具有父表的外键。当子表行中的列具有特定值时,我需要更新父表的状态列。每个父母都有多个子记录,在某些情况下没有。是否可以使用Oracle SQL实现此目的,而无需使用PL / SQL。这可能吗,有人可以解释一下吗?在某些情况下,我必须根据两列子表记录更新父表行的列。

我的确切问题是:我有两张桌子学生,MARKS。 MARKS有一个名为STUDENT_ID的学生FK.MARKS具有STUDENT记录的行数,具体取决于不同的科目(MARKS有一个FK到SUBJECTS),并有一个名为MARKS_OBTAINED的列。我必须检查如果每个主题的一个学生的MARKS_OBTAINED(即他在MARKS中的所有记录)的值为100,则将STUDENT表的列RANK更新为值'Merit'。这个查询:

update STUDENT
      set RANK = 'Merit'
      where   exists ( select *
                         from MARKS
                        where MARKS.STUDENT_ID = STUDENT.ID
                          and MARKS.MARKS_OBTAINED  = 100)
      and not exists ( select *
                         from MARKS
                        where MARKS.STUDENT_ID = STUDENT.ID
                          and MARKS.MARKS_OBTAINED != 100)

更新所有在任何科目中有100分的学生。它不排除非100标记的记录。因为它在MARKS中为STUDENT传递行,其中MARKS中的一个记录有100个MARKS_OBTAINED但其他记录少于100个标记,但由于STUDENT在一个主题中获得了100个标记,因此其RANK也将更新。要求是,如果任何STUDENT记录在MARKS_OBTAINED列中具有非100值的MARKS记录,则此STUDENT记录应从查询中排除。

3 个答案:

答案 0 :(得分:6)

完全重写

这是一个完整的重写,以使我的例子符合OQ的修订问题。不幸的是,Manish实际上没有运行我原来的解决方案,否则他们会意识到以下断言是错误的:

  

您的解决方案会返回所有这些   任何人都有100分的学生   学科。它不排除记录   没有100分。

这里有六名学生和他们的分数。

SQL> select * from student
  2  /

        ID RANK
---------- ----------
         1 normal
         2 normal
         3 normal
         4 normal
         5 normal
         6 normal

6 rows selected.

SQL> select * from marks
  2  /

 COURSE_ID STUDENT_ID       MARK
---------- ---------- ----------
         1          1        100
         2          1        100
         1          2        100
         2          2         99
         1          4        100
         2          5         99
         1          6         56
         2          6         99

8 rows selected.

SQL>

学生#1有两门课程,分数为100.学生#4只有一门课程但标记为100.学生#2在一门课程中有100分,但在另一门课程中只有99分拍摄。其他学生在任何课程中都没有得分100分。哪些学生将被授予“优点?”

SQL> update student s
  2      set s.rank = 'merit'
  3      where exists ( select null
  4                     from marks m
  5                     where m.student_id = s.id
  6                     and m.mark = 100 )
  7      and not exists ( select null
  8                       from marks m
  9                       where m.student_id = s.id
 10                       and m.mark != 100)
 11  /

2 rows updated.

SQL>
SQL> select * from student
  2  /

        ID RANK
---------- ----------
         1 merit
         2 normal
         3 normal
         4 merit
         5 normal
         6 normal

6 rows selected.

SQL>

而且!只有那些在所有课程中有100分的学生才会被更新。永远不要低估AND的力量。

所以教学是:一盎司的测试值得16吨的假设。

答案 1 :(得分:1)

现在你的问题有点过于含糊,无法真正回答。如果没有子节点,父行会发生什么?如果某些子行具有特定值而不是所有子行,会发生什么?在两列的情况下,需要什么样的子/值组合(每列的值是相同的值还是唯一的?是AND关系还是OR关系)?等...

无论如何,假设需要在给定域中至少有一个具有值的子行,这应该是相当简单的:

update PARENT set STATUS = 'whatever'
 where ID in (
     select parent_id from CHILD
      where value_col in ('your', 'specific', 'values', 'here')
 );

这种一般模式很容易扩展到多列情况(只是在内部where子句中添加一个额外的AND或ORed条件),也可以在负面情况下添加(将where ID in更改为where ID not in

如果此更新的性能存在问题,您可能需要查看触发器 - 以子表上稍微插入的速度为代价,您可以持续更新父表,而无需运行此更新声明定期。这非常有效,因为检查每个子行的逻辑基本上分布在子表的每个单独插入或更新上。当然,如果这些子修改对性能至关重要,或者如果孩子在需要更新父级的点之间多次更改,那么这将无法正常工作。

答案 2 :(得分:1)

怎么样:

UPDATE ParentTable
   SET StatusColumn = 78
 WHERE PK_Column IN
       (SELECT DISTINCT FK_Column
          FROM ChildTable AS C1
         WHERE (SELECT COUNT(*) FROM ChildTable C2
                 WHERE C1.FK_Column = C2.FK_Column) =
               (SELECT COUNT(*) FROM ChildTable C3
                 WHERE C1.FK_Column = C3.FK_Column
                   AND C3.OtherColumn = 23)
       )

我强烈怀疑有更简洁的方法可以做到这一点,但是......相关的子查询会计算子表中特定父级的行数以及子表中同一父级的行数某些过滤条件与特定值匹配。这些FK_Column值将返回到主UPDATE语句,并提供应更新状态的主键值列表。

此代码强制执行严格条件“子表中的所有匹配行都满足特定条件”。如果您的条件更简单,您的子查询可以相应更简单。