2011-03-04 14 views
5

Usando MySQL, estoy seleccionando una lista de canciones en español que me gustaría ordenar. Aquí hay una lista de nombres devueltos por la consulta:Ordenar utilizando caracteres utf en mysql o php? mejores soluciones

  • ¡Decirevilla!
  • Alhambra
  • 123 Pasitos
  • África
  • Arroz
  • Decir

La lista ordenada debe tener este aspecto:

  • 123 Pasitos
  • África
  • Alhambra
  • Arroz
  • ¡Decirevilla!
  • Decir

Después de toda la investigación que he leído, he concluido que no hay manera razonable de lograr esto usando MySQL. Probé collation, charset, etc ... pero no hay forma de que el carácter ¡,?, Etc. ... pueda ordenarse según el resultado deseado. Incluso el Á no está ordenado de la manera que quiero ...

Pregunta 1: ¿Es esta una conclusión razonable?

Creo que la única manera de lograr esto es pasar los resultados a una matriz en php y luego ordenar la matriz usando una función personalizada ... todo esto usando la función de usort (necesito ordenar por valor y no lo hago) t se preocupa por mantener la asociación de claves). Algo similar a esto:

function normalize($a, $b) { 
    if ($a == $b) { 
    return 0; 
    } 

    return ($a < $b) ? -1 : 1; 
} 


$tracks = array(); 

while ($row = $result->fetch_assoc()) { 
    $tracks[] = $row; 
} 

usort($tracks, 'normalize'); 

Pregunta 2: ¿Es esta la mejor manera de lograr una clasificación personalizada?

Aquí es donde yo estoy golpeando una pared:

Pregunta 3: No tengo idea de cómo crear la función de normalizar para ordenar los nombres de acuerdo a mis necesidades. ¿Cómo ignoro ciertos caracteres (¡,?, ',!, ¿) Y cómo reemplazo otros caracteres con el equivalente natural (Á -> A, É -> E, etc.) Creo que al ignorar ciertos caracteres y reemplazando otros, puedo lograr la clasificación que estoy buscando ...

Pregunta 4: ¿Todo esto tiene sentido? ¿Estoy en el camino correcto?

Gracias de antemano por todos sus consejos. Marco

Respuesta

1

Puede add your own collation a MySQL. Entonces podrías ignorar los personajes que no te importen, quitar los acentos según sea necesario y, en general, ordenar las cosas de la manera que desees.

Hacer la colación destrozada en el lado del cliente (es decir, en PHP en lugar de en la base de datos) no será tan rápido como hacerlo en la base de datos. Este enfoque también fallará miserablemente tan pronto como tenga que agregar LIMIT y OFFSET cláusulas a su consulta. No estoy seguro de si las intercalaciones personalizadas hacen lo correcto para MAX() funciones similares, pero hacer la intercalación desarticulada en PHP ciertamente no lo hará a menos que desee pasar la tabla completa, ordenarla y luego tomar solo una entrada.

Por lo tanto, consideraría realizar la recopilación fuera de la base de datos como último recurso.

Otra opción, si no desea construir su propia intercalación, es construir una columna artificial en su tabla que se ordene correctamente. Puede usar una función normalize() en PHP-land (algo así como el punto de partida razonable de Jacob) y mantener el resultado en la base de datos como una columna llamada, por ejemplo, sortable_title; luego ORDER BY sortable_title haría el truco. Lo que quiere una función normalize() PHP que produce una lista como esta (sin puntuacion, todo en minúsculas, acentos despojados, ...):

  • 123 Pasitos
  • África
  • alhambra
  • arroz
  • decirevilla
  • Decir

Para que un simple AS CII-betical sort hará Lo correcto. Por supuesto, tendría que inicializar sortable_title al hacer INSERT y regenerarlo durante las ACTUALIZACIONES, pero eso debería ser bastante directo si su código está encapsulado adecuadamente.

Pregunta 4: Creo que voy a estar en desacuerdo con Jacob y le digo que no va en la dirección correcta al mover la recopilación fuera de la base de datos. No estoy diciendo que estés completamente equivocado, pero es mejor que dejes que MySQL maneje la clasificación, a pesar de que puedas terminar ayudándote con algo como el truco sortable_title descrito anteriormente.

+0

¿Puedo agregar mi propia recopilación a MySQL si estoy en un host compartido? – Marco

+0

@Marco: Eso depende del proveedor de alojamiento, pero probablemente me inclinaría hacia "probablemente no". Si no puede, el enfoque 'sortable_title' hará el trabajo casi igual de bien. –

+1

Acabo de terminar de programar ambos métodos y el que tiene sortable_title es mucho, mucho más rápido. He agregado un temporizador y los resultados promedio para la solución mysql: 0.009 segundos ... solución php: 0.12 segundos. Lo extraño es que he almacenado la lista en la memoria caché (utilizando el método ob_start() ..) y el almacenamiento en caché se nota más lento ... Supongo que, en este caso específico, la apertura del archivo en caché es más lenta que la ejecución de una consulta. ..Hace que te preguntes que el almacenamiento en memoria caché en php no siempre es necesario ... – Marco

0

Pregunta 2. Esa es una buena manera de lograr una clasificación personalizada, entonces el único trabajo real que tiene que hacer es en la función de comparación.

Pregunta 3. Podría valer la pena convertir la cadena en su equivalente ASCII usando iconv. Que puede convertir UTF-8 en ASCII y usar translit, coincidirá con el carácter que no se puede convertir directamente a algo que se le parezca.

i.e.Á -> A, É -> E, etc.

Una vez convertido, puede eliminar los caracteres con los que no desea ordenar con preg_replace o str_replace.

Aquí hay un ejemplo de una función de comparación que puede usar.

function normalize_string($string) { 
    $ascii = iconv("utf-8","ascii//TRANSLIT", $string); 
    return str_replace(array('!', "'", '?'), '', $ascii); 

    // or 

    return preg_replace('/[!\'?]/', '', $ascii); 

    // or depending on how much you do want to replace... \W => any "non-word" character 

    return preg_replace('/\W/', '', $ascii); 
} 

function custom_str_cmp($a, $b) { 
    return strcmp(normalize_string($a), normalize_string($b)); 
} 

usort($tracks, 'custom_str_cmp'); 

Pregunta 4. Sí.

Cuestiones relacionadas