问题不是太具体,但不确定如何很好地解释问题。 我的数据库中有表,字段中有名称。 如果名称以某些字母开头,我想以这种方式对名称进行排序,先进行排序,依此类推。 我现在拥有的是
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”开头两个针对不同案例的单独案例陈述,这只是向我显示了相同的结果
答案 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 FIRST
或NULLS 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)