Un besoin courant lors de l'utilisation d'une base de données est d'accéder aux enregistrements dans l'ordre. Par exemple, si j'ai un blog, je veux pouvoir réorganiser mes articles de blog dans un ordre arbitraire. Ces entrées ont souvent beaucoup de relations, donc une base de données relationnelle semble logique.
La solution courante que j'ai vue est d'ajouter une colonne entière 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);
Ensuite, nous pouvons trier les lignes par order
pour les obtenir dans le bon ordre.
Cependant, cela semble maladroit:
- Si je veux déplacer l'enregistrement 0 au début, je dois réorganiser chaque enregistrement
- Si je veux insérer un nouvel enregistrement au milieu, je dois réorganiser chaque enregistrement après
- Si je veux supprimer un enregistrement, je dois réorganiser chaque enregistrement après
Il est facile d'imaginer des situations comme:
- Deux enregistrements ont le même
order
- Il existe des écarts
order
entre les enregistrements
Cela pourrait se produire assez facilement pour un certain nombre de raisons.
Voici l'approche adoptée par des applications comme Joomla:
Vous pourriez dire que l'interface ici est mauvaise, et qu'au lieu que les humains éditent directement les nombres, ils devraient utiliser des flèches ou glisser-déposer - et vous auriez probablement raison. Mais dans les coulisses, la même chose se produit.
Certaines personnes ont proposé d'utiliser une décimale pour stocker la commande, de sorte que vous pouvez utiliser "2,5" pour insérer un enregistrement entre les enregistrements de l'ordre 2 et 3. Et bien que cela aide un peu, c'est sans doute encore plus compliqué car vous pouvez vous retrouver avec décimales bizarres (où vous arrêtez-vous? 2,75? 2,875? 2,8125?)
Existe-t-il une meilleure façon de stocker la commande dans une table?
la source
orders
et le ddl.Réponses:
Non, il existe un moyen plus simple.
C'est vrai, sauf si vous utilisez un type de données qui prend en charge les valeurs "entre". Les types flottants et numériques vous permettent de mettre à jour une valeur à, disons, 2,5. Mais varchar (n) fonctionne aussi. (Pensez 'a', 'b', 'c'; puis pensez 'ba', 'bb', 'bc'.)
Non, il existe un moyen plus simple. Supprimez simplement la ligne. Les lignes restantes seront toujours triées correctement.
Une contrainte unique peut empêcher cela.
Les lacunes n'ont aucun effet sur la façon dont un dbms trie les valeurs dans une colonne.
Vous ne vous arrêtez pas avant d' avoir . Le dbms n'a aucun problème à trier les valeurs qui ont 2, 7 ou 15 places après la virgule décimale.
Je pense que votre vrai problème est que vous souhaitez voir les valeurs dans l'ordre trié sous forme d'entiers. Vous pouvez le faire.
la source
with cte as (select *,row_number() over (order by sort_order desc) as row from test) update cte set sort_order=row;
C'est très simple. Vous devez avoir une structure de "trou de cardinalité":
Vous devez avoir 2 colonnes:
integer
bigint
( pasdouble
)Insérer / mettre à jour
order = round(max_bigint / 2)
.order = round("order of first record" / 2)
order = round("max_bigint - order of last record" / 2)
4) Lors de l'insertion au milieu, réglezorder = round("order of record before - order of record after" / 2)
Cette méthode a une très grande cardinalité. Si vous avez une erreur de contrainte ou si vous pensez que vous avez une petite cardinalité, vous pouvez reconstruire la colonne d'ordre (normaliser).
En situation maximale avec normalisation (avec cette structure) vous pouvez avoir un "trou de cardinalité" en 32 bits.
N'oubliez pas de ne pas utiliser de types à virgule flottante - l'ordre doit être une valeur précise!
la source
Généralement, la commande est effectuée en fonction de certaines informations contenues dans les enregistrements, le titre, l'ID ou tout ce qui est approprié pour cette situation particulière.
Si vous avez besoin d'une commande spéciale, l'utilisation d'une colonne entière n'est pas aussi mauvaise que cela puisse paraître. Par exemple, pour faire de la place pour un disque pour aller en 5e place, vous pouvez faire quelque chose comme:
update table_1 set place = place + 1 where place > 5
.Si tout va bien vous pouvez déclarer la colonne pour être
unique
et peut-être avoir une procédure pour faire des réarrangements "atomiques". Les détails dépendent du système mais c'est l'idée générale.la source
On s'en fout? Ces chiffres ne sont là que pour que l'ordinateur puisse les traiter, peu importe le nombre de chiffres fractionnaires dont ils disposent ou leur laideur.
L'utilisation de valeurs décimales signifie que pour déplacer l'élément F entre les éléments J et K, tout ce que vous avez à faire est de sélectionner les valeurs de commande pour J et K, puis de les faire la moyenne puis de mettre à jour F. Deux instructions SELECT et une instruction UPDATE (probablement effectuées en utilisant un isolement sérialisable pour éviter blocages).
Si vous souhaitez voir des entiers plutôt que des fractions dans la sortie, calculez les entiers dans l'application cliente ou utilisez les fonctions ROW_NUMBER () ou RANK () (si votre SGBDR les inclut).
la source
Dans mon propre projet, je prévois d'essayer une solution similaire à la solution du nombre décimal, mais en utilisant des tableaux d'octets à la place:
L'idée est que vous ne pouvez jamais manquer de valeurs intermédiaires possibles, car vous ajoutez simplement un
b"\x00"
aux enregistrements impliqués si vous avez besoin de plus de valeurs. (int
est illimité en Python 3, sinon vous devrez choisir une tranche d'octets à la fin pour comparer, l'hypothèse étant que, entre deux valeurs adjacentes, les différences seraient compressées vers la fin.)Par exemple, supposons que vous ayez deux enregistrements,
b"\x00"
etb"\x01"
, et que vous souhaitiez qu'un enregistrement les sépare. Il n'y a pas de valeurs disponibles entre0x00
et0x01
, donc vous ajoutezb"\x00"
aux deux, et maintenant vous avez un tas de valeurs entre elles que vous pouvez utiliser pour insérer de nouvelles valeurs.La base de données peut facilement le trier car tout finit par ordre lexicographique. Si vous supprimez un enregistrement, il est toujours en règle. Dans mon projet, j'ai créé
b"\x00"
etb"\xff"
asFIRST
etLAST
records, cependant, afin de les utiliser comme valeurs virtuelles "de" et "à" pour ajouter / ajouter de nouveaux enregistrements:la source
J'ai trouvé cette réponse beaucoup mieux. Citant entièrement:
la source