使用来自另一个表上的多个行的数据有效地更新一个表上的行

时间:2013-05-17 17:26:25

标签: sql oracle plsql

所以在我的数据库中,我有两个表有多对一的关系。我试图通过查看'子'表上的所有行来更新'父'表(对不起,如果我没有在这里使用正确的术语)并对数据应用不同的规则集以确定要更新的值用。但我想有效地做到这一点(也就是说,很快)。

因此,请假设以下表格。

PARENT(
    ID                                 NUMBER,
    NAME                               VARCHAR(20),
    NUMBER_OF_CHILDREN                 NUMBER,
    AVERAGE_CHILD_AGE                  NUMBER,
    OLDEST_CHILD_AGE                   NUMBER,
    YOUNGEST_CHILD_AGE                 NUMBER,
    MODE_EYE_COLOR                     VARCHAR(20),
    EVERY_CHILD_MADE_A                 VARCHAR(1),
    BLOODTYPES_THAT_CAN_BE_ACCEPTED    VARCHAR(100),
    SOMETHING_COMPLEX                  COMPLEX_OBJECT_1
)

CHILD(
    ID                   NUMBER,
    PARENT_ID            NUMBER,
    AGE                  NUMBER,
    EYE_COLOR            VARCHAR(20),
    MADE_AN_A            VARCHAR(1),
    BLOODTYPE            VARCHAR(5),
    COMPLEXITY           COMPLEX_OBJECT_2
)

我使用了简化示例,需要应用的实际规则比min / max / average更复杂。现在,这些是我认为可以做到的两种方式。第一个是让程序将父ID传递给函数(我使用单独的函数,以便稍后返回并维护此代码更容易)并且每个选择子项然后处理它们。第二种方法是打开一个选择子项的游标,然后将游标传递给每个函数。

PROCEDURE UPDATE_PARENT_1 (PARENT_ID IN NUMBER)
BEGIN
    UPDATE PARENT
    SET
        NUMBER_OF_CHILDREN                = CHILD_COUNT_FUNCTION(PARENT_ID),
        AVERAGE_CHILD_AGE                 = CHILD_AGE_AVERAGE_FUNCTION(PARENT_ID),
        OLDER_CHILD_AGE                   = PICK_OLDEST_AGE_FUNCTION(PARENT_ID),
        YOUNGEST_CHILD_AGE                = PICK_YOUNGEST_AGE_FUNCTION(PARENT_ID),
        MODE_EYE_COLOR                    = MOST_OFTEN_EYE_COLOR_FUNCTION(PARENT_ID),
        BLOODTYPES_THAT_CAN_BE_ACCEPTED   = DETERMINE_BLOOD_DONOR_TYPES(PARENT_ID),
        SOMETHING_COMPLEX                 = COMPLEX_FUNCTION(PARENT_ID)
    WHERE
        ID = PARENT_ID;
END;


PROCEDURE UPDATE_PARENT_2 (PARENT_ID IN NUMBER)
    CURSOR C IS SELECT * FROM CHILD WHERE CHILD.PARENT_ID = PARENT_ID
BEGIN
    OPEN C;

    UPDATE PARENT
    SET
        NUMBER_OF_CHILDREN                = CHILD_COUNT_FUNCTION(C),
        AVERAGE_CHILD_AGE                 = CHILD_AGE_AVERAGE_FUNCTION(C),
        OLDER_CHILD_AGE                   = PICK_OLDEST_AGE_FUNCTION(C),
        YOUNGEST_CHILD_AGE                = PICK_YOUNGEST_AGE_FUNCTION(C),
        MODE_EYE_COLOR                    = MOST_OFTEN_EYE_COLOR_FUNCTION(C)
        BLOODTYPES_THAT_CAN_BE_ACCEPTED   = DETERMINE_BLOOD_DONOR_TYPES(C),
        SOMETHING_COMPLEX                 = COMPLEX_FUNCTION(C)
    WHERE
        ID = PARENT_ID;

    CLOSE C;
END;

无论哪种方式,我都觉得我正在做额外的工作。第一种方式感觉更糟,因为看起来我做了太多的选择语句(我必须应用每个规则1个,并且有很多)。第二种方式我只需要回到光标的前面,而不是做另一个选择,但它仍然感觉好像应该有一个更有效的方式。同时,oracle具有很好的幕后优化功能,因此无论哪种方式都可以通过优化方式在幕后进行优化。

所以我的问题是进行这种更新的最快方法是什么,或者我可以不担心优化它,oracle会为我处理它吗?

编辑:使示例更复杂。

3 个答案:

答案 0 :(得分:3)

你可以做除了眼睛颜色模式之外的所有事情:

UPDATE Parent
SET (Number_Of_Children, Average_Child_Age, Oldest_Child_Age, Youngest_Child_Age) = (
  SELECT COUNT(*), AVG(Age), MAX(Age), MIN(Age)
  FROM Child
  WHERE Parent.ID = Child.Parent_ID
)

我想不出一种适合模式的方法。这在SQL中通常是一个艰难的计算,我认为由于这些情况,它不适合存储在列中:

  • 三个孩子,每个孩子都有不同的眼睛颜色:这可能是无模式或三种模式(每种眼睛颜色一种)取决于你问的对象 - 有些人会回答“两个”。
  • 三个孩子,两个绿眼睛:好的,绿色是这里的模式,没问题。
  • 四个孩子,两个有棕色眼睛,两个有蓝眼睛:棕色和蓝色都是模式。

我希望这会有所帮助;可能是你努力简化这个问题,虽然很好,却让我走错了道路:)让我知道。

答案 1 :(得分:2)

首先,我无耻地借用Ed Gibb的回答。我唯一的补充就是展示如何获得模式。

为此,我使用分析函数而不是聚合。大多数新列都是相同的,只有over (partition by parent_id)子句。最里面的子查询还包括具有给定眼睛颜色的儿童数量的计数。下一级子查询按该值排序,最外层选择其中一行 - 这将具有模式。

UPDATE Parent
    SET (Number_Of_Children, Average_Child_Age, Oldest_Child_Age, Youngest_Child_Age
         Mode_Eye_Color) =
         (select cnt, avg_age, min_age, max_age, eyecolor 
          from (select cnt, avg_age, min_age, max_age, eyecolor
                       ROW_NUMBER() over (order by cnt_ec desc) as seqnum
                from (select COUNT(*) over (partition by Parent_id) as cnt,
                             AVG(Age) over (partition by Parent_id) as avg_age,
                             MIN(Age) over (partition by Parent_id) as min_age,
                             MAX(Age) over (partition by Parent_id) as max_age,
                             COUNT(*) over (partition by Parent_id, eyecolor) as cnt_ec,
                             eyecolor
                      from Child
                      where Parent.ID = Child.Parent_ID
                     ) t
               ) t
          where seqnum = 1
         )

答案 2 :(得分:1)

除了更标准的MIN(),MAX()等之外,您还可以使用各种STATS_ *函数。如果这些仍然不够,您可以创建用户定义的聚合函数。 (示例SQL取自另一个答案)

UPDATE Parent
SET (Number_Of_Children, Average_Child_Age, Oldest_Child_Age,
     Youngest_Child_Age, MODE_EYE_COLOR, BLOODTYPES_THAT_CAN_BE_ACCEPTED,
     SOMETHING_COMPLEX ) = 
(
  SELECT COUNT(*), AVG(Age), MAX(Age), MIN(Age), STATS_MODE(EYE_COLOR),
    ListBloodTypes(BLOODTYPE), ComplexCombine(SOMETHING_COMPLEX)
  FROM Child
  WHERE Parent.ID = Child.Parent_ID
)

您的用户定义的聚合函数然后需要使用:Using User-Defined Aggregate Functions作为指南来定义ListBloodTypes和ComplexCombine。