按字母顺序排列

order by case alphabetical ordering

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

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

现在这个 returns 正如我希望看到的那样,以 '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' 开头的不同情况使用 2 个单独的 case 语句,它只显示相同的结果

多做一列,material using triggers or volatile using expression only executed when select is 运行,然后用它来排序。

对于二次排序,使用名称的原始组成部分,而不是将两个名称组合在一起的表达式,从而破坏了信息。

示例: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
   )
 )

注意重要区别:我的辅助表达式是"ranking"一个。它产生几个预定义排名之一,因此将 "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   -     |

为了额外调整缺少姓名或姓氏的行的位置,您还可以使用 NULLS FIRSTNULLS LAST 选项,如 https://firebirdsql.org/file/documentation/reference_manuals/user_manuals/html/nullguide-sorts.html

的 Firebird 文档中所述

然而,这种方法的问题是,在足够大的 table 上,您将无法使用基于姓名和姓氏构建的索引进行排序,而必须求助于 un -sorted 提取数据(在读取 QUERY PLAN 时又名 NATURAL SORT),然后将其分类到磁盘上的临时文件中。对于足够大的数据,这可能会变得非常缓慢且消耗大量数据。

您可以尝试通过创建 "index by the expression" 来改善它,并在其中使用您的排名表达式。并希望 FB 优化器将使用它(对于像 CASE 这样的冗长表达式来说非常棘手)。坦率地说,您可能仍然没有它(至少我没有设法使 FB 2.1 在那里利用逐个表达式的索引)。

您可以 "materialize" 将排名表达式放入常规 SmallInt Not Null 列而不是 COMPUTED BY 列,并使用 TRIGGERBEFORE UPDATE OR INSERT 类型保留该列填充了适当的数据。然后您可以在该常规列上创建常规索引。虽然它会向每行添加两个字节,但增长并不多。

但即便如此,具有 非常少的不同值 的索引也不会增加太多价值,它将具有 "low selectivity"。此外,按表达式索引不能是 compound 一个(意思是,包括表达式后面的其他列)。

因此,对于大数据,您实际上最好将三个不同的查询融合在一起。添加脚手架,如果你还没有这样做的话:

create index i58647579_names on T58647579 ( firstname, lastname )

然后你可以像这样做三重select:

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

虽然你会遍历 table 三次 - 你会通过预先排序的索引来完成:

PLAN (S1 T58647579 ORDER I58647579_NAMES INDEX (I58647579_NAMES))
PLAN (S2 T58647579 ORDER I58647579_NAMES INDEX (I58647579_NAMES))
PLAN (S3 T58647579 ORDER I58647579_NAMES)