我在SQLite中排序歌曲(在Android上)。我想订购它们:
我有1& 2工作(见下文)。但是,除了为每个字符调用replace()
之外,我无法弄清楚如何替换每个字符(字母,数字和空格除外)。
除了〜{32}来电replace()
之外,还有办法吗?
(ASCII值33-47,58-64,91-96,123-126)
这是一张测试表。理想情况下,值'n'应该按顺序出现。 (不,您不能通过n
;)
create table songs (n integer, name text);
insert into songs (n,name) values (6,'I''ll Be That Girl');
insert into songs (n,name) values (24,'1969');
insert into songs (n,name) values (9,'La Moldau');
insert into songs (n,name) values (20,'Pule');
insert into songs (n,name) values (7,'I''m a Rainbow Too');
insert into songs (n,name) values (21,'5 Years');
insert into songs (n,name) values (18,'Pressure');
insert into songs (n,name) values (13,'Lagan');
insert into songs (n,name) values (1,'any old wind that blows');
insert into songs (n,name) values (17,'Poles Apart');
insert into songs (n,name) values (8,'Imagine');
insert into songs (n,name) values (14,'Last Stop before Heaven');
insert into songs (n,name) values (3,'I Before E Except After C');
insert into songs (n,name) values (4,'i do, i do, i do');
insert into songs (n,name) values (22,'99 Luftballons');
insert into songs (n,name) values (12,'L''accord parfait');
insert into songs (n,name) values (15,'Pluto');
insert into songs (n,name) values (19,'The Promise');
insert into songs (n,name) values (2,'(Don''t Fear) The Reaper');
insert into songs (n,name) values (10,'L.A. Nights');
insert into songs (n,name) values (23,'911 is a Joke');
insert into songs (n,name) values (5,'Ichthyosaurs Are Awesome');
insert into songs (n,name) values (11,'Labradors are Lovely');
insert into songs (n,name) values (16,'P.O.D.-Boom');
这是1和1的解决方案2以上:
SELECT n
FROM songs
ORDER BY
CASE WHEN name GLOB '[0-9]*' THEN 1
ELSE 0
END,
CASE WHEN name GLOB '[0-9]*' THEN CAST(name AS INT)
ELSE name
END
COLLATE NOCASE
对于此测试集,它按以下顺序生成结果:2,1,3,4,6,7,5,8,12,10,9,11,13,14,16,15,17,18,20,19,21,22,23,24
我可以通过手动替换为每个不需要的角色修复此特定测试集:
SELECT n
FROM songs
ORDER BY
CASE WHEN name GLOB '[0-9]*' THEN 1
ELSE 0
END,
CASE WHEN name GLOB '[0-9]*' THEN CAST(name AS INT)
ELSE
replace(
replace(
replace(
replace(name,'.',''),
'(',''
),
'''',''
),
' ',' '
)
END
COLLATE NOCASE
答案 0 :(得分:5)
我会在表格中添加一个名为" SortingName"或者其他的东西。插入时计算此值,理想情况下不是在SQL中,而是在更高级别的语言中进行所有这些良好的字符串操作。
我并没有真正理解这个数字。我想你能做的最简单的事就是在插入之前提取数字并将其放入另一列,例如" SortingNumber"。
然后简单地这样排序:
Order By
SortingName,
SortingNumber
(或者相反。)
另一个优点是性能。您经常在编写数据时更频繁地读取数据。您甚至可以在这两个排序列上创建索引,如果在查询中计算它,通常是不可能的。
答案 1 :(得分:3)
在我看来,最高性能的方法是创建一个触发器来填充名为sort_key
的新字段。您将需要一个主键。
CREATE TABLE songs (n INTEGER, name TEXT,
sort_key TEXT,
ID INTEGER PRIMARY KEY AUTOINCREMENT);
CREATE TRIGGER songs_key_trigger
AFTER INSERT ON songs FOR EACH ROW
BEGIN n
Declare @sort_key as varchar(255)
-- calculate and call here your slugify function
-- to fill sort_key from 'new.n' and 'new.name'
UPDATE songs
SET sort_key = @sort_key
WHERE ID = new.ID;
END
意识到这种方法索引友好,您可以在新列上创建索引以避免表格全扫描操作。
答案 2 :(得分:3)
第一个解决方案(可以修改数据库和应用程序):
将单个列添加到您的表中,例如solumntForSorting。 然后在你的应用程序插入之前,将你的第二个条件(“结尾的前导数字,整数值。”)连接为0或1,歌曲名称首先从不需要的符号“清除”。 所以在solumntForSorting上,你会得到这样的结果: 0Im Rainbow Too 和 1911是一个笑话。
第二种解决方案(只能修改应用程序时):
如果必须对排除某些符号的数据进行排序,并且不允许更改数据库,则会因为过滤不需要的值而选择较慢的数据。大部分开销都是CPU时间和内存。
从我的观点来看,使用替换函数是单调乏味的,这就是为什么我建议将CTE与你想要删除的值列表一起使用,例如:'。','。',';','(',' )','''',' - ')。 CTE将像多次替换一样庞大,但更容易修改和维护。
试试这个解决方案:
WITH RECURSIVE
ordering_name_substr(len, name, subsstr, hex_subsstr, number)
AS (SELECT length(name), name, substr(name, 1, 1), hex(substr(name, 1, 1)), 1
FROM songs
UNION ALL
SELECT len, name, substr(name, number + 1, 1),
hex(substr(name, number + 1, 1)), number + 1
FROM ordering_name_substr WHERE number < len),
last_order_cretaria(value, old_name)
AS (select GROUP_CONCAT(subsstr, ''), name
from ordering_name_substr
where hex_subsstr not in
('28', '29', '2C', '2E', '27') group by name )
SELECT S.n, S.name
FROM songs AS S LEFT JOIN last_order_cretaria AS OC
ON S.name = OC.old_name
ORDER BY
CASE WHEN name GLOB '[0-9]*' THEN 1
ELSE 0
END,
CASE WHEN name GLOB '[0-9]*' THEN CAST(name AS INT)
ELSE
OC.value
END
COLLATE NOCASE
在列表('28', '29', '2C', '2E', '27')
中,您可以在订购时考虑要避开的ASCII代码值(十六进制)。
您也可以尝试使用值本身,例如:('.', '.', ';', '(', ')', '''', '-')
。
WITH RECURSIVE
ordering_name_substr(len, name, subsstr, number)
AS (SELECT length(name), name, substr(name, 1, 1), 1
FROM songs
UNION ALL
SELECT len, name, substr(name, number + 1, 1),
number + 1
FROM ordering_name_substr WHERE number < len),
last_order_cretaria(value, old_name)
AS (select GROUP_CONCAT(subsstr, ''), name
from ordering_name_substr
where subsstr not in
('.', '.', ';', '(', ')', '''', '-') group by name )
SELECT S.n, S.name
FROM songs AS S LEFT JOIN last_order_cretaria AS OC
ON S.name = OC.old_name
ORDER BY
CASE WHEN name GLOB '[0-9]*' THEN 1
ELSE 0
END,
CASE WHEN name GLOB '[0-9]*' THEN CAST(name AS INT)
ELSE
OC.value
END
COLLATE NOCASE
为了使这种排序快速而简单,您必须能够更改数据库和应用程序。
答案 3 :(得分:2)
如果您被允许创建函数,这就是我创建的函数(取自How to strip all non-alphabetic characters from string in SQL Server?并稍作修改):
Create Function [dbo].[RemoveNonAlphaNumericCharacters](@Temp VarChar(1000))
Returns VarChar(1000)
AS
Begin
Declare @KeepValues as varchar(50)
Set @KeepValues = '%[^a-zA-Z0-9\s]%'
While PatIndex(@KeepValues, @Temp) > 0
Set @Temp = Stuff(@Temp, PatIndex(@KeepValues, @Temp), 1, '')
Return @Temp
End
这符合您的#3要求,并从字符串中删除所有垃圾,然后您的查询将如下所示:
SELECT n
FROM songs
ORDER BY
CASE WHEN [dbo].[RemoveNonAlphaNumericCharacters](name) GLOB '[0-9]*' THEN 1
ELSE 0
END,
CASE WHEN [dbo].[RemoveNonAlphaNumericCharacters](name) GLOB '[0-9]*' THEN CAST(name AS INT)
ELSE [dbo].[RemoveNonAlphaNumericCharacters](name)
END
COLLATE NOCASE
它看起来不漂亮,可能没有最佳性能。我可能会这样做,斯特凡建议的。解析你的歌曲名称并将修剪后的名称插入一个单独的列中,仅用于订购(当然还有该列的索引)。这应该是最好的解决方案。
答案 4 :(得分:2)
您可以使用sqlite3 Android NDK Bindings通过使用JNI调用来访问完整的sqlite3 c API。
然后您可以Define New Collating Sequences使用sqlite3_create_collation_v2()
和相关功能。{/ p>
此方法不会更改数据库,因为只会在当前数据库连接上覆盖排序规则。所以它满足了这个要求,因为它是有效的,如果数据库是只读的。
注意我说你可以。我不是说你应该!权衡这种方法的优缺点,因为在大多数情况下,这可能不值得额外的努力。