C'est un problème sur lequel j'ai passé des heures à étudier dans le passé. Cela me semble être quelque chose qui aurait dû être abordé par les solutions SGBDR modernes , mais pour l'instant je n'ai rien trouvé qui réponde vraiment à ce que je considère être un besoin incroyablement courant dans toute application Web ou Windows avec un back-end de base de données.
Je parle de tri dynamique. Dans mon monde fantastique, cela devrait être aussi simple que quelque chose comme:
ORDER BY @sortCol1, @sortCol2
C'est l'exemple canonique donné par les développeurs novices de SQL et de procédures stockées sur tous les forums sur Internet. «Pourquoi n'est-ce pas possible? ils demandent. Invariablement, quelqu'un finit par leur parler de la nature compilée des procédures stockées, des plans d'exécution en général et de toutes sortes d'autres raisons pour lesquelles il n'est pas possible de mettre un paramètre directement dans une ORDER BY
clause.
Je sais ce que certains d'entre vous pensent déjà: «Alors laissez le client faire le tri». Naturellement, cela décharge le travail de votre base de données. Dans notre cas cependant, nos serveurs de base de données ne transpirent même pas 99% du temps et ils ne sont même pas encore multicœurs ni aucune des myriades d'améliorations de l'architecture système qui se produisent tous les 6 mois. Pour cette seule raison, avoir nos bases de données gérer le tri ne serait pas un problème. De plus, les bases de données sont trèsbon pour trier. Ils sont optimisés pour cela et ont eu des années pour bien faire les choses, le langage pour le faire est incroyablement flexible, intuitif et simple et surtout tout écrivain SQL débutant sait comment le faire et plus important encore, il sait comment le modifier, faire des changements, faire de la maintenance, etc. Lorsque vos bases de données sont loin d'être taxées et que vous souhaitez simplement simplifier (et raccourcir!) le temps de développement, cela semble être un choix évident.
Ensuite, il y a le problème du Web. J'ai joué avec JavaScript qui fera le tri côté client des tableaux HTML, mais ils ne sont inévitablement pas assez flexibles pour mes besoins et, encore une fois, comme mes bases de données ne sont pas trop taxées et peuvent faire le tri très très facilement, je ont du mal à justifier le temps qu'il faudrait pour réécrire ou lancer mon propre trieur JavaScript. Il en va généralement de même pour le tri côté serveur, bien qu'il soit probablement déjà largement préféré à JavaScript. Je ne suis pas de ceux qui aiment particulièrement les frais généraux des DataSets, alors poursuivez-moi.
Mais cela ramène le fait que ce n'est pas possible - ou plutôt, pas facilement. J'ai fait, avec les systèmes antérieurs, une manière incroyablement pirate d'obtenir un tri dynamique. Ce n'était pas joli, ni intuitif, simple ou flexible et un écrivain SQL débutant serait perdu en quelques secondes. Cela semble déjà être non pas tant une «solution» qu'une «complication».
Les exemples suivants ne sont pas destinés à exposer des meilleures pratiques ou un bon style de codage ou quoi que ce soit, ni ne reflètent mes capacités en tant que programmeur T-SQL. Ils sont ce qu'ils sont et j'admets pleinement qu'ils sont déroutants, de mauvaise forme et tout simplement piratés.
Nous passons une valeur entière comme paramètre à une procédure stockée (appelons simplement le paramètre "sort") et à partir de là, nous déterminons un tas d'autres variables. Par exemple ... disons que le tri est 1 (ou la valeur par défaut):
DECLARE @sortCol1 AS varchar(20)
DECLARE @sortCol2 AS varchar(20)
DECLARE @dir1 AS varchar(20)
DECLARE @dir2 AS varchar(20)
DECLARE @col1 AS varchar(20)
DECLARE @col2 AS varchar(20)
SET @col1 = 'storagedatetime';
SET @col2 = 'vehicleid';
IF @sort = 1 -- Default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'asc';
SET @sortCol2 = @col2;
SET @dir2 = 'asc';
END
ELSE IF @sort = 2 -- Reversed order default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'desc';
SET @sortCol2 = @col2;
SET @dir2 = 'desc';
END
Vous pouvez déjà voir comment si je déclarais plus de variables @colX pour définir d'autres colonnes, je pourrais vraiment faire preuve de créativité avec les colonnes à trier en fonction de la valeur de "sort" ... pour l'utiliser, cela finit généralement par ressembler à ce qui suit clause incroyablement désordonnée:
ORDER BY
CASE @dir1
WHEN 'desc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir1
WHEN 'asc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END,
CASE @dir2
WHEN 'desc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir2
WHEN 'asc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END
C'est évidemment un exemple très dépouillé. Le vrai truc, puisque nous avons généralement quatre ou cinq colonnes sur lesquelles prendre en charge le tri, chacune avec une éventuelle colonne secondaire ou même une troisième colonne à trier en plus de cela (par exemple, date décroissante puis triée secondairement par nom croissant) et chaque support bi- tri directionnel qui double effectivement le nombre de cas. Ouais ... ça devient poilu très vite.
L'idée est que l'on pourrait "facilement" changer les cas de tri de telle sorte que le Vehicleid soit trié avant le storagedatetime ... mais la pseudo-flexibilité, au moins dans cet exemple simple, s'arrête vraiment là. Essentiellement, chaque cas qui échoue à un test (car notre méthode de tri ne s'applique pas cette fois-ci) renvoie une valeur NULL. Et ainsi vous vous retrouvez avec une clause qui fonctionne comme suit:
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
Vous avez eu l'idée. Cela fonctionne car SQL Server ignore effectivement les valeurs nulles dans l'ordre des clauses. C'est incroyablement difficile à maintenir, comme toute personne ayant une connaissance de base de SQL peut probablement le voir. Si j'ai perdu l'un de vous, ne vous sentez pas mal. Il nous a fallu beaucoup de temps pour le faire fonctionner et nous sommes toujours confus en essayant de le modifier ou d'en créer de nouveaux comme celui-ci. Heureusement, il n'a pas besoin de changer souvent, sinon cela deviendrait rapidement "ne vaut pas la peine".
Pourtant, cela a fonctionné.
Ma question est alors: y a-t-il une meilleure façon?
Je suis d'accord avec des solutions autres que celles de la procédure stockée, car je réalise que ce n'est peut-être pas la voie à suivre. De préférence, j'aimerais savoir si quelqu'un peut le faire mieux dans la procédure stockée, mais sinon, comment gérez-vous tous permettre à l'utilisateur de trier dynamiquement des tables de données (bidirectionnelles également) avec ASP.NET?
Et merci d'avoir lu (ou du moins écrémé) une si longue question!
PS: Soyez heureux de ne pas avoir montré mon exemple de procédure stockée prenant en charge le tri dynamique, le filtrage dynamique / recherche de texte des colonnes, la pagination via ROWNUMBER () OVER, ET essayez ... attraper avec la restauration des transactions sur les erreurs ... "de la taille d'un géant" ne commence même pas à les décrire.
Mettre à jour:
- Je voudrais éviter le SQL dynamique . Analyser une chaîne ensemble et exécuter un EXEC dessus va à l'encontre de beaucoup de l'objectif d'avoir une procédure stockée en premier lieu. Parfois, je me demande si les inconvénients de faire une telle chose n'en valent pas la peine, du moins dans ces cas spéciaux de tri dynamique. Pourtant, je me sens toujours sale chaque fois que je fais des chaînes SQL dynamiques comme ça - comme si je vis toujours dans le monde ASP classique.
- Une grande partie des raisons pour lesquelles nous voulons des procédures stockées en premier lieu est pour la sécurité . Je n'ai pas la possibilité de faire un appel sur les problèmes de sécurité, je suggère seulement des solutions. Avec SQL Server 2005, nous pouvons définir des autorisations (sur une base par utilisateur si nécessaire) au niveau du schéma sur des procédures stockées individuelles, puis refuser directement toute requête sur les tables. Critiquer les avantages et les inconvénients de cette approche est peut-être pour une autre question, mais encore une fois, ce n'est pas ma décision. Je suis juste le singe du code principal. :)
la source
Réponses:
Ouais, c'est une douleur, et la façon dont tu le fais ressemble à ce que je fais:
Cela, pour moi, est toujours bien meilleur que la création de SQL dynamique à partir de code, qui se transforme en un cauchemar d'évolutivité et de maintenance pour les DBA.
Ce que je fais à partir du code, c'est refactoriser la pagination et le tri afin que je n'ai au moins pas beaucoup de répétition avec les valeurs de remplissage pour
@SortExpr
et@SortDir
.En ce qui concerne le SQL, gardez la conception et le formatage identiques entre les différentes procédures stockées, de sorte qu'il soit au moins soigné et reconnaissable lorsque vous apportez des modifications.
la source
Cette approche empêche les colonnes triables d'être dupliquées deux fois dans l'ordre par, et est un peu plus lisible IMO:
la source
Le SQL dynamique est toujours une option. Vous devez simplement décider si cette option est plus acceptable que ce que vous avez actuellement.
Voici un article qui montre que: http://www.4guysfromrolla.com/webtech/010704-1.shtml .
la source
Mes applications le font beaucoup mais elles construisent toutes dynamiquement le SQL. Cependant, lorsque je traite des procédures stockées, je fais ceci:
select * from dbo.fn_myData() where ... order by ...
afin que vous puissiez spécifier de manière dynamique l'ordre de tri.Alors au moins la partie dynamique est dans votre application, mais la base de données fait toujours le gros du travail.
la source
Une technique de procédure stockée (hack?) Que j'ai utilisée pour éviter le SQL dynamique pour certains travaux est d'avoir une colonne de tri unique. C'est à dire,
Celui-ci est facile à battre en soumission - vous pouvez concater des champs dans votre colonne mySort, inverser l'ordre avec des fonctions mathématiques ou de date, etc.
De préférence cependant, j'utilise mes gridviews asp.net ou d'autres objets avec un tri intégré pour faire le tri pour moi APRÈS avoir récupéré les données de Sql-Server. Ou même si ce n'est pas intégré - par exemple, les tables de données, etc. dans asp.net.
la source
Vous pouvez pirater cela de différentes manières.
Conditions préalables:
Puis insérez dans une table temporaire:
Méthode n ° 2: configurez un serveur lié à lui-même, puis sélectionnez-le en utilisant openquery: http://www.sommarskog.se/share_data.html#OPENQUERY
la source
Il peut y avoir une troisième option, puisque votre serveur a beaucoup de cycles de réserve - utilisez une procédure d'aide pour faire le tri via une table temporaire. Quelque chose comme
Attention: je n'ai pas testé cela, mais cela "devrait" fonctionner dans SQL Server 2005 (qui créera une table temporaire à partir d'un jeu de résultats sans spécifier les colonnes à l'avance.)
la source
À un moment donné, cela ne vaut-il pas la peine de s'éloigner des procédures stockées et d'utiliser simplement des requêtes paramétrées pour éviter ce type de piratage?
la source
Je suis d'accord, utilisez le côté client. Mais il semble que ce ne soit pas la réponse que vous souhaitez entendre.
Donc, c'est parfait tel quel. Je ne sais pas pourquoi vous voudriez le changer, ou même demander "Y a-t-il un meilleur moyen." Vraiment, cela devrait s'appeler "The Way". En outre, il semble fonctionner et répondre parfaitement aux besoins du projet et sera probablement suffisamment extensible pour les années à venir. Étant donné que vos bases de données ne sont pas taxées et que le tri est vraiment très simple, cela devrait le rester pour les années à venir.
Je ne transpirerais pas.
la source
Lorsque vous paginez des résultats triés, le SQL dynamique est une bonne option. Si vous êtes paranoïaque à propos de l'injection SQL, vous pouvez utiliser les numéros de colonne au lieu du nom de colonne. J'ai fait cela avant d'utiliser des valeurs négatives pour la descente. Quelque chose comme ça...
Ensuite, vous devez simplement vous assurer que le nombre est compris entre 1 et # de colonnes. Vous pouvez même étendre cela à une liste de numéros de colonne et l'analyser dans une table d'entiers en utilisant une fonction comme celle-ci . Ensuite, vous construisez l'ordre par clause comme ceci ...
Un inconvénient est que vous devez vous souvenir de l'ordre de chaque colonne côté client. Surtout lorsque vous n'affichez pas toutes les colonnes ou que vous les affichez dans un ordre différent. Lorsque le client souhaite trier, vous mappez les noms de colonnes à l'ordre des colonnes et générez la liste des entiers.
la source
Un argument contre le tri côté client est la pagination et les données de grand volume. Une fois que votre nombre de lignes dépasse ce que vous pouvez facilement afficher, vous triez souvent dans le cadre d'un saut / prise, que vous souhaitez probablement exécuter en SQL.
Pour Entity Framework, vous pouvez utiliser une procédure stockée pour gérer votre recherche de texte. Si vous rencontrez le même problème de tri, la solution que j'ai vue consiste à utiliser une procédure stockée pour la recherche, en renvoyant uniquement un jeu de clés d'identification pour la correspondance. Ensuite, ré-interrogez (avec le tri) sur la base de données en utilisant les identifiants dans une liste (contient). EF gère cela assez bien, même lorsque le jeu d'identifiants est assez volumineux. Oui, il s'agit de deux allers-retours, mais cela vous permet de toujours conserver votre tri dans la base de données, ce qui peut être important dans certaines situations, et vous empêche d'écrire une cargaison de logique dans la procédure stockée.
la source
Que diriez-vous de gérer le tri sur les éléments affichant les résultats - grilles, rapports, etc. plutôt que sur SQL?
ÉDITER:
Pour clarifier les choses puisque cette réponse a été votée à la baisse plus tôt, je vais élaborer un peu ...
Vous avez déclaré que vous connaissiez le tri côté client mais que vous vouliez vous en éloigner. C'est votre appel, bien sûr.
Ce que je tiens à souligner, cependant, c'est qu'en le faisant du côté client, vous pouvez extraire des données UNE FOIS, puis les utiliser comme vous le souhaitez - au lieu de faire plusieurs allers-retours vers le serveur à chaque fois. le tri est changé.
Votre serveur SQL n'est pas taxé pour le moment et c'est génial. Ça ne devrait pas l'être. Mais ce n'est pas parce qu'il n'est pas encore surchargé qu'il le restera pour toujours.
Si vous utilisez l'un des éléments ASP.NET les plus récents pour l'affichage sur le Web, une grande partie de ces éléments est déjà intégré.
Vaut-il la peine d'ajouter autant de code à chaque procédure stockée juste pour gérer le tri? Encore une fois, votre appel.
Ce n'est pas moi qui serai finalement en charge de le soutenir. Mais réfléchissez à ce qui sera impliqué lorsque des colonnes seront ajoutées / supprimées dans les différents ensembles de données utilisés par les procédures stockées (nécessitant des modifications des instructions CASE) ou lorsque soudainement au lieu de trier par deux colonnes, l'utilisateur décide qu'il en a besoin de trois - vous obligeant à mettre à jour maintenant chacune de vos procédures stockées qui utilise cette méthode.
Pour moi, cela vaut la peine d'obtenir une solution côté client fonctionnelle et de l'appliquer à la poignée d'affichages de données destinés aux utilisateurs et d'en finir avec elle. Si une nouvelle colonne est ajoutée, elle est déjà gérée. Si l'utilisateur souhaite trier par plusieurs colonnes, il peut trier par deux ou vingt d'entre elles.
la source
Désolé, je suis en retard à la fête, mais voici une autre option pour ceux qui veulent vraiment éviter le SQL dynamique, mais veulent la flexibilité qu'il offre:
Au lieu de générer dynamiquement le SQL à la volée, écrivez du code pour générer un processus unique pour chaque variation possible. Ensuite, vous pouvez écrire une méthode dans le code pour examiner les options de recherche et lui faire choisir le processus approprié à appeler.
Si vous n'avez que quelques variantes, vous pouvez simplement créer les procs à la main. Mais si vous avez beaucoup de variations, au lieu d'avoir à les maintenir toutes, vous maintiendrez simplement votre générateur de proc à la place pour qu'il les recrée.
En tant qu'avantage supplémentaire, vous obtiendrez de meilleurs plans SQL pour de meilleures performances en le faisant également.
la source
Cette solution pourrait ne fonctionner qu'en .NET, je ne sais pas.
Je récupère les données dans le C # avec l'ordre de tri initial dans la clause SQL order by, je mets ces données dans un DataView, je les cache dans une variable de session et je les utilise pour créer une page.
Lorsque l'utilisateur clique sur un en-tête de colonne pour trier (ou page, ou filtre), je ne retourne pas dans la base de données. Au lieu de cela, je retourne à mon DataView mis en cache et définit sa propriété "Sort" sur une expression que je construis dynamiquement, tout comme je le ferais en SQL dynamique. (Je fais le filtrage de la même manière, en utilisant la propriété "RowFilter").
Vous pouvez le voir / sentir qu'il fonctionne dans une démo de mon application, BugTracker.NET, à http://ifdefined.com/btnet/bugs.aspx
la source
Vous devez éviter le tri SQL Server, sauf si nécessaire. Pourquoi ne pas trier sur le serveur d'applications ou côté client? De plus .NET Generics fait un tri exceptionnel
la source