按大小写字母顺序排序

时间:2019-10-31 16:26:00

标签: firebird

问题不是太具体,但不确定如何很好地解释问题。 我的数据库中有表,字段中有名称。 如果名称以某些字母开头,我想以这种方式对名称进行排序,先进行排序,依此类推。 我现在拥有的是

SELECT
  (T.firstname||' '||T.lastname) as Full_Name
FROM 
  TABLE T
ORDER BY
  CASE
    WHEN LPAD(T.firstname, 1) = 'J' THEN T.firstname
    WHEN LPAD(T.firstname, 1) = 'B' THEN T.firstname
  END DESC,
Full_Name ASC

现在按我想的样子返回,以“ J”开头的名称先被排序,然后是“ B”,然后是其余的。 但是,结果看起来像

What I get    What I want

Full_Name     Full_Name
----------    ----------
Junior MR     James A
John Doe      Joe Bob
Joe Bob       John Doe
James A       Junior MR
Brad T        B Test
Bob Joe       Bb Test
Bb Test       Bob Joe
B Test        Brad T
A Test        A Test
Aa Test       Aa Test
AFLKJASDFJ    AFLKJASDFJ
Ann Doe       Ann Doe

但是我想要的是J和B也要按字母顺序排序,现在它正在按相反的字母顺序排序。 我如何指定案例内的顺序? 我尝试以“ J”和“ B”开头两个针对不同案例的单独案例陈述,这只是向我显示了相同的结果

1 个答案:

答案 0 :(得分:2)

只需添加一列,使用triggers编写材料,或仅在运行select时执行使用表达式创建的volatile,然后将其用于排序。

对于二级排序,请使用名称的原始组成部分,而不是将两个名称合并在一起的表达式,这样会破坏信息。

示例:https://dbfiddle.uk/?rdbms=firebird_3.0&fiddle=fbf89b3903d3271ae6c55589fd9cfe23

 create table T (
   firstname varchar(10),
   lastname varchar(10),
   fullname computed by ( 
       Coalesce(firstname, '-') || ' ' || Coalesce(T.lastname, '-')  
   ),
   sorting_helper computed by (
     CASE WHEN firstname starting with 'J' then 100
          WHEN firstname starting with 'B' then 50
          ELSE 0
     END
   )
 )

通知 的重要区别:我的助手表达是“排名”。它产生几个预定义的排名之一,因此将“ James”和“ Joe”放入具有完全相同排名值的同一个箱中。您的表达仍然会产生名称本身,因此错误地保持了名称之间的差异。但是您不希望出现这种差异,而是告诉您希望所有以J开头的名称都向上移动,然后按照通常的规则在它们之间进行排序。因此,按照您说的做,做一个将所有J名称拉在一起的表达式,而不必在两者之间进行区分。

 insert into T
   select 
     'John', 'Doe'  
   from rdb$database union all select
     'James', 'A'  
   from rdb$database union all select
     'Aa ', 'Test'  
   from rdb$database union all select
     'Ann', 'Doe'  
   from rdb$database union all select
     'Bob', 'Joe'  
   from rdb$database union all select
     'Brad', 'Test'  
   from rdb$database union all select
     NULL, 'Smith'  
   from rdb$database union all select
     'Ken', NULL  
   from rdb$database 
8 rows affected
 select * from T
FIRSTNAME | LASTNAME | FULLNAME    | SORTING_HELPER
:-------- | :------- | :---------- | -------------:
John      | Doe      | John  Doe   |            100
James     | A        | James A     |            100
Aa        | Test     | Aa    Test  |              0
Ann       | Doe      | Ann   Doe   |              0
Bob       | Joe      | Bob   Joe   |             50
Brad      | Test     | Brad  Test  |             50
null      | Smith    | - Smith     |              0
Ken       | null     | Ken   -     |              0
 Select FullName from T order by sorting_helper desc, firstname asc, lastname asc
| FULLNAME    |
| :---------- |
| James A     |
| John  Doe   |
| Bob   Joe   |
| Brad  Test  |
| - Smith     |
| Aa    Test  |
| Ann   Doe   |
| Ken   -     |

或者没有computed-by

 Select FullName from T order by (CASE WHEN firstname starting with 'J' then 0
          WHEN firstname starting with 'B' then 1
          ELSE 2
     END) asc, firstname asc, lastname asc
| FULLNAME    |
| :---------- |
| James A     |
| John  Doe   |
| Bob   Joe   |
| Brad  Test  |
| - Smith     |
| Aa    Test  |
| Ann   Doe   |
| Ken   -     |

要对缺少名称或姓氏的行的位置进行额外调整,还可以使用https://firebirdsql.org/file/documentation/reference_manuals/user_manuals/html/nullguide-sorts.html上Firebird文档中所述的NULLS FIRSTNULLS LAST选项。


但是,这种方法在足够大的表上存在的问题是,您将无法使用基于名称和姓氏构建的索引进行排序,相反,您将不得不采用未排序的数据提取(aka NATURAL SORT读取QUERY PLAN时),然后将其分类为磁盘上的临时文件。对于足够大的数据,这可能会变得非常缓慢且耗时大量。

您可以尝试使用此处的排名表达式来创建“按表达式建立索引”,以使其更好。并希望FB优化程序可以使用它(对于像CASE这样的冗长表达式,这非常棘手)。坦白说,您可能仍然会没有它(至少我没有设法使FB 2.1在那里利用按个案表达的索引)。

您可以将排名表达式“具体化”为常规的SmallInt Not Null列而不是COMPUTED BY列,并使用TRIGGER类型的BEFORE UPDATE OR INSERT保持该列中填充适当的数据。然后,您可以在该常规列上创建一个常规索引。虽然它将为每行添加两个字节,但增长的幅度并不大。

但是即使那样,具有非常少的不同值的索引也不会增加太多的值,它将具有“低选择性”。此外,按表达式索引不能为compound(表示,包括表达式后面的其他列)。

因此,对于大数据,您最好将三个不同的查询融合在一起。添加脚手架(如果尚未添加):

create index i58647579_names on T58647579 ( firstname, lastname )

然后您可以像这样三重选择:

WITH S1 as (
  select FullName from T58647579
  where firstname starting with 'J' 
  order by firstname asc, lastname asc
), S2 as (
  select FullName from T58647579
  where firstname starting with 'B'
  order by firstname asc, lastname asc
), S3 as (
  select FullName from T58647579
  where (firstname is null)
     or (  (firstname not starting with 'J')
       and (firstname not starting with 'B')
        )
  order by firstname asc, lastname asc
)
SELECT * FROM S1
   UNION ALL
SELECT * FROM S2
   UNION ALL
SELECT * FROM S3

当您遍历表三次时,您可以通过预先排序的索引来做到这一点:

PLAN (S1 T58647579 ORDER I58647579_NAMES INDEX (I58647579_NAMES))
PLAN (S2 T58647579 ORDER I58647579_NAMES INDEX (I58647579_NAMES))
PLAN (S3 T58647579 ORDER I58647579_NAMES)
相关问题