2015-09-17 22 views
14

我在SQLite中(在Android上)排序歌曲。我想訂購他們:ORDER BY只能在SQLite中使用字母數字字符

  1. 不區分大小寫
  2. ,並在最後領先位數,由整數值。
  3. 沒有標點符號(例如括號,句號,連字符,撇號)

我有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 & 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

如果有幫助,我可以依靠SQLite 3.8.6或更高版本。目前,我只針對Android L,並且很快將針對Android M. – Phrogz

+0

@ Phrogz ..爲什麼不嘗試採用原始字符串長度和標點符號替換爲'''的字符串長度(空字符串),並在這個問題中排序第3位? –

+0

我不認爲你得到了我。這是我想傳達的。對於一個字符串'ab,c'它將是len(orig_string)是4並且len(用''替換的orig_string_with_punctuation是3)。所以這樣你得到的diff就是'1'。而對於沒有標點符號的字符串,這個差異將是0.所以你可以在'order by'子句中使用這些差異。希望你能找到我。 –

回答

5

我會在表格中添加一個名爲「SortingName」的東西。在插入時計算此值,理想情況下不是在SQL中,而是在具有所有這些漂亮的字符串操作的較高級別語言中。

我真的不明白這個數字的東西。我猜你可以做的最簡單的事情是在插入之前提取數字並將其放入另一列,如「SortingNumber」。

然後簡單的排序是這樣的:(或者反過來)

Order By 
    SortingName, 
    SortingNumber 

另一個優點是性能。你通常會在你寫的時候更頻繁地閱讀數據。你甚至可以在這兩個排序列上創建索引,如果你在查詢中計算它通常是不可能的。

+1

對不起,從我的角度來看,數據庫是隻讀的。該表是根據找到的歌曲的元數據動態生成的。有可能我可以用這些信息添加第二個表格,但是隨着新歌曲可用或消失,我需要擔心表格不同步。 – Phrogz

+0

聽起來像數據庫觸發器的絕望使用案例之一... –

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 

它看起來不漂亮,可能不具有最佳性能。我很可能會這樣做,Stefan建議。解析你的歌曲名稱,並將修剪後的歌名插入一個單獨的列中,以便進行排序(當然,還有該列的索引)。這應該是最好的解決方案。

+0

感謝您的想法。我不認爲這是可能的,因爲這不是我的數據庫,而是由媒體經理提供的。 http://stackoverflow.com/a/8283265 – Phrogz

+0

@Progro我在看標籤產品時應該更加小心。我想那裏沒有其他真正的方法,而不是用30多個替換手動去除所有的垃圾。 –

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

您可以使用sqlite3 Android NDK Bindings通過使用JNI調用來訪問完整的sqlite3 c API。

然後,您可以通過使用sqlite3_create_collation_v2()和相關功能Define New Collating Sequences

此方法不會更改數據庫,因爲排序規則僅在當前數據庫連接上被覆蓋。因此,如果數據庫是隻讀的,它就能滿足這一要求。

注意我說你可以。我不是說你應該!權衡這種方法的優缺點,因爲在大多數情況下,這可能是不值得的額外努力。

3

第一溶液(當DB和應用可以被修改):

添加到表格中單柱例如solumntForSorting。 然後在插入之前在您的應用程序中,將第二個條件(「以最後的前導數字,按整數值」)連接爲0或1,以便將歌曲名稱首先從不需要的符號中「清除」。 所以在solumntForSorting你會得到像這樣的東西:0我是彩虹太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 

I have tested on sqlfiddle.

在列表('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 

要使此分類工作快速簡單,您必須能夠更改您的數據庫和應用程序。

相關問題