Una necesidad común cuando se usa una base de datos es acceder a los registros en orden. Por ejemplo, si tengo un blog, quiero poder reordenar mis publicaciones en un orden arbitrario. Estas entradas a menudo tienen muchas relaciones, por lo que una base de datos relacional parece tener sentido.
La solución común que he visto es agregar una columna entera order
:
CREATE TABLE AS your_table (id, title, sort_order)
AS VALUES
(0, 'Lorem ipsum', 3),
(1, 'Dolor sit', 2),
(2, 'Amet, consect', 0),
(3, 'Elit fusce', 1);
Luego, podemos ordenar las filas order
para obtenerlas en el orden correcto.
Sin embargo, esto parece torpe:
- Si quiero mover el registro 0 al inicio, tengo que reordenar cada registro
- Si quiero insertar un nuevo registro en el medio, tengo que reordenar cada registro después
- Si quiero eliminar un registro, tengo que reordenar cada registro después de él
Es fácil imaginar situaciones como:
- Dos registros tienen lo mismo
order
- Hay lagunas en los
order
registros entre
Esto podría suceder con bastante facilidad por varias razones.
Este es el enfoque que adoptan aplicaciones como Joomla:
Se podría argumentar que la interfaz aquí es mala, y que en lugar de que los humanos editen números directamente, deberían usar flechas o arrastrar y soltar, y probablemente tenga razón. Pero detrás de escena, está sucediendo lo mismo.
Algunas personas han propuesto usar un decimal para almacenar el orden, de modo que pueda usar "2.5" para insertar un registro entre los registros en el orden 2 y 3. Y aunque eso ayuda un poco, podría decirse que es aún más complicado porque puede terminar con decimales extraños (¿dónde se detiene? 2.75? 2.875? 2.8125?)
¿Hay una mejor manera de almacenar el pedido en una mesa?
fuente
orders
y el ddl.Respuestas:
No, hay una manera más simple.
Eso es cierto, a menos que use un tipo de datos que admita valores "entre". Los tipos flotante y numérico le permiten actualizar un valor a, digamos, 2.5. Pero varchar (n) también funciona. (Piense 'a', 'b', 'c'; luego piense 'ba', 'bb', 'bc'.)
No, hay una manera más simple. Solo borra la fila. Las filas restantes aún se ordenarán correctamente.
Una restricción única puede evitar eso.
Las brechas no tienen efecto sobre cómo un dbms ordena los valores en una columna.
No te detienes hasta que tienes que hacerlo. El dbms no tiene problemas para ordenar valores que tienen 2, 7 o 15 lugares después del punto decimal.
Creo que su verdadero problema es que le gustaría ver los valores en orden ordenado como enteros. Usted puede hacer eso.
fuente
with cte as (select *,row_number() over (order by sort_order desc) as row from test) update cte set sort_order=row;
Es muy simple. Debe tener una estructura de "agujero de cardinalidad":
Necesitas tener 2 columnas:
integer
bigint
( nodouble
)Insertar / actualizar
order = round(max_bigint / 2)
.order = round("order of first record" / 2)
order = round("max_bigint - order of last record" / 2)
4) Al insertar en el medio, configureorder = round("order of record before - order of record after" / 2)
Este método tiene una cardinalidad muy grande. Si tiene un error de restricción o si cree que tiene una pequeña cardinalidad, puede reconstruir la columna de orden (normalizar).
En una situación máxima con normalización (con esta estructura) puede tener un "agujero de cardinalidad" en 32 bits.
Recuerde no utilizar tipos de punto flotante: ¡el orden debe ser un valor preciso!
fuente
En general, el pedido se realiza de acuerdo con cierta información en los registros, el título, la identificación o lo que sea apropiado para esa situación en particular.
Si necesita un pedido especial, usar una columna entera no es tan malo como parece. Por ejemplo, para dejar espacio para que un registro ingrese al 5to lugar, podría hacer algo como:
update table_1 set place = place + 1 where place > 5
.Con suerte, puede declarar que la columna es
unique
y tal vez tener un procedimiento para hacer reordenamientos "atómicos". Los detalles dependen del sistema, pero esa es la idea general.fuente
¿A quien le importa? Estos números solo están ahí para que la computadora los maneje, por lo que no importa cuántos dígitos fraccionales tengan o cuán feos nos parezcan.
El uso de valores decimales significa que para mover el elemento F entre los elementos J y K, todo lo que necesita hacer es seleccionar los valores de orden para J y K, luego promediarlos y luego actualizar F. Dos declaraciones SELECT y una declaración UPDATE (probablemente hechas usando aislamiento serializable para evitar puntos muertos).
Si desea ver enteros en lugar de fracciones en la salida, calcule enteros en la aplicación cliente o use las funciones ROW_NUMBER () o RANK () (si su RDBMS los incluye).
fuente
En mi propio proyecto, planeo probar una solución similar a la solución de números decimales, pero en su lugar utilizo matrices de bytes:
La idea es que nunca puede quedarse sin posibles valores intermedios porque y simplemente agrega un
b"\x00"
a los registros involucrados si necesita más valores. (int
no está limitado en Python 3, de lo contrario, tendría que elegir una porción de los bytes al final para comparar, suponiendo que, entre dos valores adyacentes, las diferencias se agruparían hacia el final).Por ejemplo, digamos que tiene dos registros,
b"\x00"
yb"\x01"
desea un registro entre ellos. No hay valores disponibles entre0x00
y0x01
, por lo que agregab"\x00"
a ambos, y ahora tiene un montón de valores entre ellos que puede usar para insertar nuevos valores.La base de datos puede ordenarlo fácilmente porque todo termina en orden lexicográfico. Si elimina un registro, todavía está en orden. Sin embargo, en mi proyecto, hice
b"\x00"
yb"\xff"
comoFIRST
yLAST
registros, para usarlos como valores virtuales "de" y "a" para anteponer / agregar nuevos registros:fuente
Encontré esta respuesta mucho mejor. Citando completamente:
fuente