Cadena de orden SQL como número

137

Tengo números guardados VARCHARen una base de datos MySQL. No puedo hacerlos INTdebido a otras circunstancias dependientes.

Los toma como caracteres, no como números mientras los ordena.

En la base de datos tengo

1 2 3 4 5 6 7 8 9 10...

En mi página muestra una lista ordenada como esta:

1 10 2 3 4 5 6 7 8 9

¿Cómo puedo hacer que parezca ordenado por números ascendentes?

Jamol
fuente
55
Seguramente debería haber almacenado los datos como un número, en lugar de una cadena.
Oded
@Oded Puede tener algunos datos arbitrarios (por ejemplo, configuraciones), que desea ordenar por su valor numérico solo en casos muy especiales.
Glutexo
artículo muy interesante - es para MSSQL pero debería ser relativamente similar para MySQL: essentialsql.com/use-sql-server-to-sort-alphanumeric-values
AquaAlex

Respuestas:

256

Si es posible, debe cambiar el tipo de datos de la columna a un número si solo almacena números de todos modos.

Si no puede hacer eso, envíe el valor de su columna a un integer explícitamente con

select col from yourtable
order by cast(col as unsigned)

o implícitamente, por ejemplo, con una operación matemática que obliga a una conversión a número

select col from yourtable
order by col + 0

Por cierto, MySQL convierte cadenas de izquierda a derecha. Ejemplos:

string value  |  integer value after conversion
--------------+--------------------------------
'1'           |  1
'ABC'         |  0   /* the string does not contain a number, so the result is 0 */
'123miles'    |  123 
'$123'        |  0   /* the left side of the string does not start with a number */
juergen d
fuente
3
ordenar por col * 1 funciona perfecto. ¿Cuál es la magia detrás de esto? Lo siento, no soy profesional, así que esta podría ser una pregunta tonta, pero ¿Cómo * 1 hace que cambie a número?
Jamol
12
MySQL convierte automáticamente la cadena en un número para hacer la multiplicación con1
juergen d
1
La primera opción es la más apropiada, la segunda tiene un paso adicional, pero no obstante funciona.
Manatax
55
y si ha mezclado una cadena y un número y desea que ambos estén ordenados, use "ordenar por col * 1, col"
Hayden Thring
1
@superfónico: el resultado será decimal.
juergen d
70

Otra forma, sin usar un solo molde.

(Para las personas que usan JPA 2.0, donde no se permite la transmisión)

select col from yourtable
order by length(col),col

EDITAR: solo funciona para enteros positivos

Velter
fuente
9
esta es buena, eso funcionó con una columna que también contiene int y string
Sruit A.Suk
1
Esto no funcionará en todos los casos. Fuera de la parte superior de la cabeza, los casos que no funcionarán son mezclas de números enteros con; números negativos, números con ceros a la izquierda, números con fracciones y palabras y números combinados.
WonderWorker
3
gran solución sin ningún tipo de casting y malabarismo, +1
Maksim Luzik
no parecía funcionar a primera vista, ¡pero funcionó! ¡Gracias!
try2fly.b4ucry
19

Otra forma simple

ORDER BY ABS(column_name)

Parth Solanki
fuente
17

La columna con la que estoy ordenando tiene cualquier combinación de alfa y numérico, por lo que utilicé las sugerencias en esta publicación como punto de partida y se me ocurrió esto.

DECLARE @tmp TABLE (ID VARCHAR(50));
INSERT INTO @tmp VALUES ('XYZ300');
INSERT INTO @tmp VALUES ('XYZ1002');
INSERT INTO @tmp VALUES ('106');
INSERT INTO @tmp VALUES ('206');
INSERT INTO @tmp VALUES ('1002');
INSERT INTO @tmp VALUES ('J206');
INSERT INTO @tmp VALUES ('J1002');

SELECT ID, (CASE WHEN ISNUMERIC(ID) = 1 THEN 0 ELSE 1 END) IsNum
FROM @tmp
ORDER BY IsNum, LEN(ID), ID;

Resultados

ID
------------------------
106
206
1002
J206
J1002
XYZ300
XYZ1002

Espero que esto ayude

Losbear
fuente
Simplemente puede usar en 1 - ISNUMERIC(ID)lugar de (CASE WHEN ISNUMERIC(ID) = 1 THEN 0 ELSE 1 END)cambiar de 0 a 1 y viceversa.
S.Serpooshan
7

Esto funciona para mi.

select * from tablename
order by cast(columnname as int) asc
Patrick De Guzman
fuente
¿puedes explicar porque?
Slavik
la respuesta más simple, que hace exactamente lo que se necesita, convierte la cadena en un int, luego ordena por ese int en orden ascendente, sin cosas funky :)
denford mutseriwa
4

Otra forma de convertir.

Si tiene un campo de cadena, puede transformarlo o su parte numérica de la siguiente manera: agregue ceros a la izquierda para hacer que todas las cadenas enteras tengan la misma longitud.

ORDER BY CONCAT( REPEAT(  "0", 18 - LENGTH( stringfield ) ) , stringfield ) 

u ordenar por parte de un campo algo como 'tensymbols13', 'tensymbols1222' etc.

ORDER BY CONCAT( REPEAT(  "0", 18 - LENGTH( LEFT( stringfield , 10 ) ) ) , LEFT( stringfield , 10 ) ) 
shukshin.ivan
fuente
2

Estaba buscando también un campo de clasificación que tenga un prefijo de letra. Aquí es lo que descubrí la solución. Esto podría ayudar a quién está buscando la misma solución.

Valores de campo:

FL01,FL02,FL03,FL04,FL05,...FL100,...FL123456789

select SUBSTRING(field,3,9) as field from table order by SUBSTRING(field,3,10)*1 desc

SUBSTRING(field,3,9) Puse 9 porque 9 es suficiente para mantener valores enteros de máximo 9 dígitos.

Entonces el resultado será 123456789 123456788 123456787 ... 100 99 ... 2 1

usuario2400083
fuente
2

Esto manejará números negativos, fracciones, cadenas, todo:

ORDER BY ISNUMERIC(col) DESC, Try_Parse(col AS decimal(10,2)), col;
Amir
fuente
2

Podría ayudar a quién está buscando la misma solución.

select * from tablename ORDER BY ABS(column_name)
Samir Lakhani
fuente
0

Si está utilizando AdonisJS y tiene ID mixtos como ABC-202, ABC-201 ..., puede combinar consultas sin procesar con Query Builder e implementar la solución anterior ( https://stackoverflow.com/a/25061144/4040835 ) como sigue:

const sortField =
  'membership_id'
const sortDirection =
  'asc'
const subquery = UserProfile.query()
  .select(
    'user_profiles.id',
    'user_profiles.user_id',
    'user_profiles.membership_id',
    'user_profiles.first_name',
    'user_profiles.middle_name',
    'user_profiles.last_name',
    'user_profiles.mobile_number',
    'countries.citizenship',
    'states.name as state_of_origin',
    'user_profiles.gender',
    'user_profiles.created_at',
    'user_profiles.updated_at'
  )
  .leftJoin(
    'users',
    'user_profiles.user_id',
    'users.id'
  )
  .leftJoin(
    'countries',
    'user_profiles.nationality',
    'countries.id'
  )
  .leftJoin(
    'states',
    'user_profiles.state_of_origin',
    'states.id'
  )
  .orderByRaw(
    `SUBSTRING(:sortField:,3,15)*1 ${sortDirection}`,
    {
      sortField: sortField,
    }
  )
  .paginate(
    page,
    per_page
  )

NOTAS: En esta línea: SUBSTRING(:sortField:,3,15)*1 ${sortDirection},

  1. '3' representa el número de índice del último carácter no numérico antes de los dígitos. Si su ID mixta es "ABC-123", su número de índice será 4.
  2. '15' se usa para capturar la mayor cantidad de dígitos posible después del guión.
  3. '1' realiza una operación matemática en la subcadena que efectivamente convierte la subcadena en un número.

Ref. 1: puede leer más sobre enlaces de parámetros en consultas sin procesar aquí: https://knexjs.org/#Raw-Bindings Ref. 2: Adonis Raw Queries: https://adonisjs.com/docs/4.1/query-builder# _raw_queries

Ndianabasi
fuente
-3

Modifique su campo para que sea INT en lugar de VARCHAR.

No puedo hacerlos INT debido a otras circunstancias dependientes.

Luego arregle las circunstancias dependientes primero. De lo contrario, está trabajando en torno al problema subyacente real. Usar un CAST de MySQL es una opción, pero está enmascarando su esquema incorrecto que debería corregirse.

Cañada
fuente