按字母顺序排列
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 FIRST
或 NULLS 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
列,并使用 TRIGGER
的 BEFORE 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)
问题不是太具体但不知道如何解释好问题。 我的数据库中有 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 FIRST
或 NULLS LAST
选项,如 https://firebirdsql.org/file/documentation/reference_manuals/user_manuals/html/nullguide-sorts.html
然而,这种方法的问题是,在足够大的 table 上,您将无法使用基于姓名和姓氏构建的索引进行排序,而必须求助于 un -sorted 提取数据(在读取 QUERY PLAN
时又名 NATURAL SORT
),然后将其分类到磁盘上的临时文件中。对于足够大的数据,这可能会变得非常缓慢且消耗大量数据。
您可以尝试通过创建 "index by the expression" 来改善它,并在其中使用您的排名表达式。并希望 FB 优化器将使用它(对于像 CASE
这样的冗长表达式来说非常棘手)。坦率地说,您可能仍然没有它(至少我没有设法使 FB 2.1 在那里利用逐个表达式的索引)。
您可以 "materialize" 将排名表达式放入常规 SmallInt Not Null
列而不是 COMPUTED BY
列,并使用 TRIGGER
的 BEFORE 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)