为什么使用lower()会改变结果集的顺序?

时间:2013-04-24 07:45:49

标签: oracle10g

我有一个表格,用于存储有关用户的信息。该表具有以下结构:

CREATE TABLE PERSONS 
(
  ID NUMBER(20, 0) NOT NULL,
  FIRSTNAME VARCHAR2(40), 
  LASTNAME VARCHAR2(40),
  BIRTHDAY DATE, 
  CONSTRAINT PERSONEN_PK PRIMARY KEY 
  (ID)
  ENABLE 
);

插入一些测试数据后:

SET DEFINE OFF;
Insert into PERSONS (ID,FIRSTNAME,LASTNAME,BIRTHDAY) values ('1','Max','Mustermann',to_date('31.10.89','DD.MM.RR'));
Insert into PERSONS (ID,FIRSTNAME,LASTNAME,BIRTHDAY) values ('2','Max','Mustermann',to_date('31.10.89','DD.MM.RR'));
Insert into PERSONS (ID,FIRSTNAME,LASTNAME,BIRTHDAY) values ('3','Carl','Carlchen',to_date('01.01.12','DD.MM.RR'));
Insert into PERSONS (ID,FIRSTNAME,LASTNAME,BIRTHDAY) values ('4','Max','Mustermann',to_date('31.10.89','DD.MM.RR'));
Insert into PERSONS (ID,FIRSTNAME,LASTNAME,BIRTHDAY) values ('5','Max','Mustermann',to_date('31.10.89','DD.MM.RR'));
Insert into PERSONS (ID,FIRSTNAME,LASTNAME,BIRTHDAY) values ('6','Carl','Carlchen',to_date('01.01.12','DD.MM.RR'));

我想选择给定用户的所有重复项。我们以“Max Mustermann”为例:

SELECT p.id,p.firstname,p.lastname,p.birthday
  FROM persons p
  WHERE p.firstname = 'Max'
    AND p.lastname = 'Mustermann'
    AND p.birthday = to_date('31.10.1989','dd.mm.yyyy')
  ORDER BY p.firstname,p.lastname;

这给我一个这样的结果:

id  first   last        birthday
=================================
1   Max     Mustermann  31.10.89
2   Max     Mustermann  31.10.89
4   Max     Mustermann  31.10.89
5   Max     Mustermann  31.10.89

我想做一个不区分大小写的比较,所以我使用lower(和trim)更改查询,如下所示:

SELECT p.id,p.firstname,p.lastname,p.birthday
  FROM persons p
  WHERE lower(trim(p.firstname)) = lower(trim('mAx '))
    AND lower(trim(p.lastname)) = lower(trim('  musteRmann  '))
    AND p.birthday = to_date('31.10.1989','dd.mm.yyyy')
  ORDER BY p.lastname,p.firstname;

现在惊讶于订单已经改变了!

id  first   last        birthday
=================================
1   Max     Mustermann  31.10.89
5   Max     Mustermann  31.10.89
4   Max     Mustermann  31.10.89
2   Max     Mustermann  31.10.89

为什么订单会改变,只需使用lower()(在没有trim()的情况下使用时结果相同)!?我可以通过将id列添加到ORDER BY来获得稳定的排序。但是,lower()不应该对排序产生影响吗?

使用ORDER BY的id列进行解决方法:

SELECT p.id,p.firstname,p.lastname,p.birthday
  FROM persons p
  WHERE p.firstname = 'Max'
    AND p.lastname = 'Mustermann'
    AND p.birthday = to_date('31.10.1989','dd.mm.yyyy')
  ORDER BY p.firstname,p.lastname,p.id;

SELECT p.id,p.firstname,p.lastname,p.birthday
  FROM persons p
  WHERE lower(trim(p.firstname)) = lower(trim('mAx '))
    AND lower(trim(p.lastname)) = lower(trim('  musteRmann  '))
    AND p.birthday = to_date('31.10.1989','dd.mm.yyyy')
  ORDER BY p.lastname,p.firstname,p.id;

2 个答案:

答案 0 :(得分:2)

如果要排序的值相同,那么DBMS可以自由选择任何感觉正确的订单(如果没有指定order by,则可以自由选择任何订单)。

由于顺序中列的所有值都相同,因此生成的顺序不稳定。获得稳定订单的唯一方法是包含一个唯一列作为关系的附加订单条件 - 正是您在添加id列时所执行的操作。

  

为什么订单会改变,只需使用lower()

从技术角度来看,我猜应用lower()改变了执行计划,从而改变了数据的访问路径。

但是再次(只是为了确保):对相同值的排序永远不能保证稳定的订单!

答案 1 :(得分:1)

没有order by clause没有订购。有时看起来可能会有(group by在旧版本中欺骗了很多人,但这只是巧合,不能依赖。在你的情况下,你是按一些列排序,但你希望重复在该命令中,隐式地进一步排序,这不会发生 - 或者至少不能依赖。

在这种情况下,Oracle可能恰好按照您插入它们的顺序检索第一个查询的行,纯粹是因为它是如何从块中读取数据的副作用,order by在它们中对它们进行排序没有实际改变它们(或者很可能它在内部跳过order by步骤,如果它意识到它没有意义;解释计划会告诉你)。

如果您更改了创建记录的顺序:

...
Insert into PERSONS (ID,FIRSTNAME,LASTNAME,BIRTHDAY) values
('5','Max','Mustermann',to_date('31.10.89','DD.MM.RR'));
Insert into PERSONS (ID,FIRSTNAME,LASTNAME,BIRTHDAY) values
('4','Max','Mustermann',to_date('31.10.89','DD.MM.RR'));
...

然后结果'order'也会改变:

SELECT p.id,p.firstname,p.lastname,p.birthday
  FROM persons p
  WHERE p.firstname = 'Max'
    AND p.lastname = 'Mustermann'
    AND p.birthday = to_date('31.10.1989','dd.mm.yyyy')
  ORDER BY p.firstname,p.lastname;

        ID FIRSTNAME            LASTNAME             BIRTHDAY
---------- -------------------- -------------------- ---------
         1 Max                  Mustermann           31-OCT-89
         2 Max                  Mustermann           31-OCT-89
         5 Max                  Mustermann           31-OCT-89
         4 Max                  Mustermann           31-OCT-89

一旦你拥有了这个功能,即使记录以id顺序插入(内部与DB无关),事情也会发生变化,足以让那个快乐的事故走出窗外。 lower()并没有改变顺序,你只是不再幸运了。

除非完全order by子句中指定,否则您不能指望或依赖订单。