ORDER BY alphanumeric characters only in SQLite

后端 未结 5 1166
再見小時候
再見小時候 2021-02-05 15:24

I am sorting songs in SQLite (on Android). I want to order them:

  1. Case-insensitive
  2. With leading-digits at the end, by integer value.
  3. Without punct
5条回答
  •  终归单人心
    2021-02-05 15:53

    The first solution (when DB and application can be modified):

    Add to your table single column e.g. solumntForSorting. Then on your application before inserting, concatenate your second condition ("With leading-digits at the end, by integer value.") as 0 or 1 to song name which first 'was cleaned' from undesired symbols. So on solumntForSorting you will get something like this: 0Im a Rainbow Too and 1911 is a Joke.

    The second solution (when only application can be modified):

    If you have to sort data excluding some symbols and you are not allowed to change your DB, you will get a slower selection because of filtering undesired values. Most of the overhead will be at CPU time and memory.

    Using replace function is tedious from my point of view, that is why I suggest using CTE with list of values you want to drop, like this ('.', '.', ';', '(', ')', '''', '-'). CTE will be bulky like multiple replace but it is easier to modify and maintain.

    Try this solution:

     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.

    In the list ('28', '29', '2C', '2E', '27') you have values of ASCII codes (in hex) which you want to escape from be considered in ordering.

    You also can try to use values itself like: ('.', '.', ';', '(', ')', '''', '-').

    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
    

    To make this sorting work fast and simple you have to be able to change your DB and application.

提交回复
热议问题