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. :)