Je dois prendre une décision de conception sur la base de données. L'obligation est que une table de base de données a une clé primaire EM> EM> EM> Strong> appelée ID strong>. Par défaut, chaque ligne est montrée à l'utilisateur (dans web) triée ascendante par Maintenant, il est nécessaire que l'utilisateur puisse faire glisser la ligne d'interface utilisateur pour changer de séquence . Dites, faites glisser la ROM 3 et déposez-le. Donc, la séquence d'affichage tourne pour être Je me demande comment concevoir une table de base de données pour rendre cela persistant et évolutif. Ma première pensée est que chaque ligne a une séquence " Si la séquence est modifiée, alors il est mis à jour pour nouvelle valeur. Le résultat est que cela peut impliquer beaucoup de changements dans d'autres lignes. Prenant l'exemple ci-dessus, à l'origine, la table est comme ceci: p> maintenant, après la ligne de glisser avec ID 3 à la première. Sa séquence forte> est mise à jour à 0. Dans le même temps, la rangée avec ID 0, 1, 2 doit également être mise à jour. P> |id | sequence |
|0 | 0 |
|1 | 10 |
|2 | 20 |
|3 | 30 |
8 Réponses :
Comment des listes liées? : -)
CREATE TABLE item( id INT PRIMARY KEY, prev INT, next INT ); WITH RECURSIVE sequence AS ( SELECT item.id, item.prev, item.next FROM item WHERE item.prev IS NULL UNION SELECT item.id, item.prev, item.next FROM sequence INNER JOIN item ON sequence.next = item.id ) SELECT * FROM sequence;
Si cela ne se limitait pas à MySQL, je dirais que c'est la meilleure solution.
MDR vraiment? O (1) mises à jour et insertion, mais l'exécution de cette requête particulière est O (n).
- Edit: Juste pour que d'autres lisent ce post, j'ai été évacué de certains étrange rancune personnelle, pas d'inexactitude générale en ce qui concerne ce sujet, donnez cette considération lors de la lecture et de voter comment vous le souhaitez! :) p>
- Vieux: P>
La manière typique de le faire est avec votre numéro "séquence" (je l'appelle "SortOrdon"). P>
C'est vraiment facile. P>
"évolutif" ne vient pas dedans; Parce que vous avez déjà une liste de tous les nœuds que vous travaillez (glisser-déposer, comme vous le dites), alors ce que vous faites vraiment, c'est simplement échanger ces chiffres. P>
trivial. p>
Si vous faites glisser un article de la fin au début, vous ne vous attendez-vous pas à ce que tout déplace une fente au lieu de simplement échanger les deux rangées?
Eph: Bien sûr; Mais combien d'articles sont affichés? Je ne pense pas que ce soit pratique que ce soit un problème.
Hahaha. Ah clote. Vous êtes amusant.
Il est étrange de brosser cela comme trivial et de supposer qu'il n'y aura pas beaucoup de données. Évidemment, l'OP pense qu'il y aurait une question de performance, et ils ont probablement une bonne raison. Comment pouvez-vous dire "évolutif n'en viendra pas"? Comment savez-vous qu'il n'y a pas 100 000 rangées dans cette table? Et même s'il n'y a que 20, que si cette opération est effectuée sur le serveur 50 fois par seconde par divers utilisateurs?
Echanger des travaux sauf dans les deux cas de bord: (1) déplacer le dernier élément à la première position; et (2) déplacer la première position au dernier élément. Dans les deux cas, vous êtes obligé de réorganiser l'ensemble de la matrice.
L'identifiant et la séquence / sordion sont séparés et ne doivent pas dépendre du tout l'un de l'autre.
pour une fonction de déplacement / descente: Vous pouvez échanger des valeurs de séquence / de tri p>
ou p>
pour une fonctionnalité de glisser-déposer: p>
1) Établissez la nouvelle séquence / commande de commande pour l'enregistrement sélectionné.
2) Obtenez la séquence actuelle des enregistrements sélectionnés, puis mettez à jour l'enregistrement sélectionné avec le nouveau numéro. P>
3) a) Si le nouveau numéro de séquence est inférieur au numéro de séquence actuel incrémenter tous les numéros de séquence pour les enregistrements qui ont un numéro de séquence> = le nouveau numéro de séquence (exlcutant le numéro sélectionné) p>
B) si le nouveau Le numéro de séquence est au-dessus du numéro de séquence actuel, décrémenter tous les numéros de séquence ci-dessous le nouveau et supérieur à celui actuel et supérieur à celui actuel. p>
espère que cela a du sens et que j'ai bien le bon chemin (ci-dessous est le réel Mise en œuvre). P>
Je l'ai mis en œuvre dans une seule instruction SQL qui a une faible quantité de logique, pas pour les puristes, mais ses fonctionnements bien. P>
Voici un exemple (op : Vous voudrez modifier les identifiants GUID sur INTS): P>
CREATE PROCEDURE [proc_UpdateCountryRowOrder] @ID UNIQUEIDENTIFIER, @NewPosition INT AS SET NOCOUNT ON DECLARE @CurrentPosition INT DECLARE @MaximumPosition INT IF (@NewPosition < 1) SET @NewPosition = 1 SELECT @CurrentPosition = [Countries].[Order] FROM [Countries] WHERE [Countries].[ID] = @ID SELECT @MaximumPosition = MAX([Countries].[Order]) FROM [Countries] IF (@NewPosition > @MaximumPosition) SET @NewPosition = @MaximumPosition IF (@NewPosition <> @CurrentPosition) BEGIN IF (@NewPosition < @CurrentPosition) BEGIN BEGIN TRAN UPDATE [Countries] SET [Countries].[Order] = [Countries].[Order] + 1 WHERE [Countries].[Order] >= @NewPosition AND [Countries].[Order] < @CurrentPosition UPDATE [Countries] SET [Countries].[Order] = @NewPosition WHERE ID = @ID COMMIT TRAN END ELSE BEGIN BEGIN TRAN UPDATE [Countries] SET [Countries].[Order] = [Countries].[Order] - 1 WHERE [Countries].[Order] <= @NewPosition AND [Countries].[Order] > @CurrentPosition UPDATE [Countries] SET [Countries].[Order] = @NewPosition WHERE ID = @ID COMMIT TRAN END END GO
Cette approche peut mettre à jour assez quelques enregistrements pour une goutte unique. Sera-ce costy?
Je suis d'accord, tous les enregistrements doivent être mis à jour, ce qui serait coûteux mais je pense qu'il serait nécessaire d'attribuer définitivement un ordre de tri. Il suffit de penser: Que se passe-t-il si le tri a été extrait des données dans une autre table et que les deux. De cette façon, seule la table "SortOrdon" serait mise à jour? Des commentaires sur cela?
Si vous déplacez l'article 902 en position 5, vous allez devoir mettre à jour l'ancien 5-901 avec 902. Je ne pense pas que vous puissiez éviter le coût si vous voulez pouvoir le faire de manière persistante
La seule façon de savoir s'il sera trop coûteux, c'est de créer des données de test de la magnitude que vous pouvez attendre dans la vie réelle, puis essayez-la.
Je l'ai fait en renvoyant une chaîne CSV des identifiants (touches DB) dans la commande sélectionnée par l'utilisateur sur le serveur. J'ai une fonction sur mon dB qui convertira une chaîne CSV en une table avec 2 champs - l'ID et une séquence (qui est en fait un INT avec une identité). Les valeurs de champ de séquence dans cette table TMP reflètent l'ordre des éléments de la chaîne CSV. Je mettez ensuite à jour la table de données avec la nouvelle valeur de champ de séquence, correspondant à l'IDS.
Edit: Les points de prime semblaient si savoureux, je pensais que je fournirais des détails sur ma réponse. Voici le code: p> i configuré Vous aurez besoin d'une fonction qui analysde le CSV (je peux poster le mien si vous êtes intéressé, et que vous êtes intéressé. J'ai vu les autres postés ici et là). Notez que mon code suppose que vous utilisez SQL Server - la chose de séquence dépend d'un champ d'identité et la requête de mise à jour utilise une extension T-SQL (la clause 'de') - si vous utilisez d'autres dB, vous auriez besoin de faire des changements simples. p> p> @id_array code> comme variable pour tester - généralement votre interface utilisateur obtiendrait les valeurs d'identification dans leur ordre révisé et transmettez-le comme un paramètre sur un processus stocké. Le
get_id_table_from_list code> fonction analyse une chaîne de CSV dans une table avec deux colonnes 'int': [id] et [séquence]. La colonne [Séquence] est une identité. Les résultats de cette fonction travaillant sur mes données de test ressemblent à ceci: p>
La réponse évidente pour moi est d'utiliser la dernière solution que vous avez mentionnée mais avec des décimales (flotteurs). P>
Alors vous commencez par, disons: Une autre solution similaire consiste à utiliser des caractères, puis trier par des chaînes alphabétiquement. Commençant par Le seul problème que je peux penser avec ces méthodes est que finalement, vous toucherez la limite de précision du point flottant, c'est-à-dire d'essayer de trouver un nombre entre {0,1, 0,2, 0,3, 0,4, 0.5} code>. Si vous déplacez le dernier élément à entre
0.2 code> et
0.3 code> il devient
0.25 code>. Si vous le déplacez vers le haut, il devient
0.05 code>. Chaque fois que vous prenez simplement le milieu des deux chiffres de chaque côté. En d'autres termes, la moyenne des éléments précédents / suivants. P>
{1, 2, 3, 4, 5} code>, si vous déplacez les 5 entre 2 et 3, vous utiliseriez 25. Si vous faites une sorte de chaîne de la liste que vous gardez le bon ordre :
{1, 2, 25, 3, 4} code>. p>
0.0078125 code> et
0.0078124 CODE >. Quelques façons de résoudre ce problème: P>
{0,1, 0,2, 0,3, ...} li>.
0.2 code> et
0,25 code> Vous pouvez utiliser
0.23 code> au lieu du
0.225 code>. Li>.
{0.2, 0.3, 0,6} code> et souhaitez insérer après
0.2 code>, vous pouvez définir le second sur
0.4 code> et insérer le nouveau Article à
0.3 code>. li>
ul>
Bonne réponse! J'allais suggérer des chiffres de séquence avec un intervalle fixe initialement (dire (10,20,30,40) afin que vous puissiez facilement séquencer une ligne, mais la solution «flottante» est meilleure que vous pouvez toujours trouver une valeur entre les rangées avant et après.
Pas "TOUJOURS" ... vous allez finalement frapper la limite de précision du point flottant. Mais cela va probablement prendre un certain temps :)
Oui, c'est à quoi ressemble la dernière partie de la réponse. À un moment donné, vous devrez re-séquencer manuellement. J'aime l'idée de re-séquençant de petits groupes de nombres, le cas échéant.
J'ai répondu à une question similaire ici: Commandez visuellement les grands ensembles de données a > p>
Si vous déplacez de nombreux éléments, vous devez boucler et déplacer chaque article et vérifier le débordement. Mais dans l'ensemble, la logique de base est d'avoir une colonne de tri avec des lacunes pouvant être réinitialisées périodiquement. P>
Ce correctif est plus facile que vous ne le pensez. Une table Temp et une requête de mise à jour et votre fait.
CREATE TABLE #TempData ( NewSequence bigint identity(1,1), [Id] BigInt ) INSERT INTO #TempData ([Id]) SELECT [Id] FROM TableNameGoesHere ORDER BY Sequence UPDATE TableNameGoesHere SET Sequence = t2.NewSequence FROM TableNameGoesHere t1 INNER JOIN #TempData t2 ON t1.[Id] = t2.[Id] DROP TABLE #TempData
Je sais que ceci est une question de 3 ans, mais les commentaires m'ont aidé à résoudre un problème similaire et je voulais fournir mon code au cas où elle aiderait à la recherche de quelque chose de similaire.
basé sur l'échantillon de code fourni Par @Disgruntledgoat (qui, au fait, merci de votre part!) J'ai créé le code suivant écrit spécifiquement pour l'entité Framework 4.1 Code-d'abord. Il prend essentiellement trois paramètres - un objet de référentiel, l'ID d'un objet entité et une valeur booléenne pour indiquer si l'afficheur de l'entité doit être déplacé de haut en bas à l'écran. L'entité doit hériter de l'entité, ce qui signifie qu'il doit avoir une valeur d'identification et mettre en œuvre IOPORTEDENTITITY, ce qui signifie qu'il doit disposer d'une propriété de flotteur d'affichage. P>
La méthode Supplée trouve les deux entrées adjacentes (inférieures ou supérieures à la Ordre d'affichage actuel, en fonction du sens de déplacement), puis en moyenne en moyenne à leurs valeurs (d'où la nécessité du flotteur et non d'une valeur entière). Il mettra ensuite à jour le référentiel pour cette entité. Ensuite, à des fins de nettoyage, si le nombre de décimales dans la nouvelle commande d'affichage résultant dépasse 5, il utilise EF pour mettre à jour toutes les valeurs de la base de données et les réenverse à une valeur de 1, 2, 3, etc. / p>
Ce code fonctionne parfaitement pour moi et accueillerait des commentaires s'il doit être nettoyé ou refactored. P>
public abstract class Entity : IIdentifiableEntity { public int Id { get; set; } } public interface IOrderedEntity { float DisplayOrder { get; set; } } public interface IRepository<TEntity> { TEntity FindById(int id); bool InsertOrUpdate(TEntity entity); // More repository methods here... } public static class RepositoryExtenstions { public static void MoveDisplayOrder<T>(IRepository<T> repository, int id, bool moveUp) where T : Entity, IOrderedEntity { var currentStatus = repository.FindById(id); IQueryable<IOrderedEntity> adjacentStatuses; if (moveUp) adjacentStatuses = repository.All().OrderByDescending(ms => ms.DisplayOrder).Where(ms => ms.DisplayOrder < currentStatus.DisplayOrder); else adjacentStatuses = repository.All().OrderBy(ms => ms.DisplayOrder).Where(ms => ms.DisplayOrder > currentStatus.DisplayOrder); var adjacentTwoDisplayOrders = adjacentStatuses.Select(ms => ms.DisplayOrder).Take(2).ToList(); float averageOfPreviousTwoDisplayOrders; switch (adjacentTwoDisplayOrders.Count) { case 0: // It's already at the top or bottom, so don't move it averageOfPreviousTwoDisplayOrders = currentStatus.DisplayOrder; break; case 1: // It's one away, so just add or subtract 0.5 to the adjacent value if (moveUp) averageOfPreviousTwoDisplayOrders = adjacentTwoDisplayOrders[0] - 0.5F; else averageOfPreviousTwoDisplayOrders = adjacentTwoDisplayOrders[0] + 0.5F; break; default: // 2 // Otherwise, just average the adjacent two values averageOfPreviousTwoDisplayOrders = adjacentTwoDisplayOrders.Average(); break; } currentStatus.DisplayOrder = averageOfPreviousTwoDisplayOrders; repository.InsertOrUpdate(currentStatus); var floatPrecision = currentStatus.DisplayOrder.ToString().Substring(currentStatus.DisplayOrder.ToString().IndexOf('.') + 1).Length; if(floatPrecision > 5) ReorganizeDisplayOrder(repository); } public static void ReorganizeDisplayOrder<T>(IRepository<T> repository) where T : Entity, IOrderedEntity { var entities = repository.All().OrderBy(ms => ms.DisplayOrder).ToList(); float counter = 1F; foreach (var entity in entities) { entity.DisplayOrder = counter; repository.InsertOrUpdate(entity); counter++; } } }
Oui. Mysql. Mais je suppose que cela peut être une question non limitée à MySQL.