Este es un problema que he pasado horas investigando en el pasado. Me parece algo que debería haber sido abordado por las soluciones RDBMS modernas , pero hasta ahora no he encontrado nada que realmente aborde lo que veo como una necesidad increíblemente común en cualquier aplicación web o Windows con una base de datos.
Hablo de ordenación dinámica. En mi mundo de fantasía, debería ser tan simple como algo como:
ORDER BY @sortCol1, @sortCol2
Este es el ejemplo canónico dado por los novatos desarrolladores de SQL y procedimientos almacenados en todos los foros de Internet. "¿Por qué no es esto posible?" ellos preguntan. Invariablemente, eventualmente alguien viene a darles una conferencia sobre la naturaleza compilada de los procedimientos almacenados, de los planes de ejecución en general, y todo tipo de otras razones por las cuales no es posible poner un parámetro directamente en una ORDER BY
cláusula.
Sé lo que algunos de ustedes ya están pensando: "Deje que el cliente haga la clasificación, entonces". Naturalmente, esto descarga el trabajo de su base de datos. Sin embargo, en nuestro caso, nuestros servidores de bases de datos ni siquiera están sudando el 99% del tiempo y ni siquiera son multinúcleo ni ninguna de las otras miles de mejoras en la arquitectura del sistema que ocurren cada 6 meses. Solo por esta razón, hacer que nuestras bases de datos manejen la clasificación no sería un problema. Además, las bases de datos son muybueno en la clasificación Están optimizados para ello y han tenido años para hacerlo bien, el lenguaje para hacerlo es increíblemente flexible, intuitivo y simple y, sobre todo, cualquier escritor principiante de SQL sabe cómo hacerlo y, lo que es más importante, saben cómo editarlo, realizar cambios, realizar tareas de mantenimiento, etc. Cuando sus bases de datos están lejos de estar sujetas a impuestos y solo desea simplificar (¡y acortar!) el tiempo de desarrollo, esto parece una opción obvia.
Luego está el problema web. He jugado con JavaScript que hará la clasificación del lado del cliente de las tablas HTML, pero inevitablemente no son lo suficientemente flexibles para mis necesidades y, una vez más, dado que mis bases de datos no están excesivamente gravadas y pueden hacer la clasificación de manera muy fácil, me resulta difícil justificar el tiempo que llevaría volver a escribir o clasificar mi propio clasificador de JavaScript. Lo mismo ocurre generalmente con la ordenación del lado del servidor, aunque probablemente ya sea mucho más preferido que JavaScript. No soy uno que le guste particularmente los gastos generales de los DataSets, así que demándame.
Pero esto trae de vuelta el punto de que no es posible, o más bien, no es fácil. He hecho, con sistemas anteriores, una forma increíblemente hack de obtener una clasificación dinámica. No era bonito, ni intuitivo, simple o flexible, y un escritor principiante de SQL se perdería en segundos. Ya parece que esto no es tanto una "solución" sino una "complicación".
Los siguientes ejemplos no están destinados a exponer ningún tipo de mejores prácticas o buen estilo de codificación ni nada, ni son indicativos de mis habilidades como programador de T-SQL. Son lo que son y admito que son confusos, de mala forma y simplemente piratas.
Pasamos un valor entero como parámetro a un procedimiento almacenado (llamemos al parámetro simplemente "ordenar") y a partir de eso determinamos un montón de otras variables. Por ejemplo ... digamos que sort es 1 (o el valor predeterminado):
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
Ya puede ver cómo si declarara más variables @colX para definir otras columnas, realmente podría ser creativo con las columnas para ordenar en función del valor de "ordenar" ... para usarlo, generalmente termina pareciéndose a lo siguiente cláusula increíblemente desordenada:
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
Obviamente este es un ejemplo muy despojado. Lo real, ya que generalmente tenemos cuatro o cinco columnas para admitir la clasificación, cada una con una posible secundaria o incluso una tercera columna para clasificar además de eso (por ejemplo, fecha descendente y luego ordenada secundariamente por nombre ascendente) y cada una clasificación direccional que efectivamente duplica el número de casos. Sí ... se pone peludo muy rápido.
La idea es que uno podría "cambiar fácilmente" los casos de clasificación de manera que el vehículo se clasifique antes del tiempo de almacenamiento ... pero la pseudo-flexibilidad, al menos en este simple ejemplo, realmente termina allí. Esencialmente, cada caso que falla una prueba (porque nuestro método de clasificación no se aplica esta vez) presenta un valor NULL. Y así terminas con una cláusula que funciona como la siguiente:
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
Tienes la idea. Funciona porque SQL Server ignora efectivamente los valores nulos en orden por cláusulas. Esto es increíblemente difícil de mantener, como puede ver cualquier persona con conocimientos básicos de SQL. Si los he perdido, no se sienta mal. Nos llevó mucho tiempo hacerlo funcionar y todavía nos confundimos al intentar editarlo o crear otros nuevos como este. Afortunadamente, no es necesario cambiarlo con frecuencia, de lo contrario se convertiría rápidamente en "no vale la pena".
Sin embargo , funcionó.
Mi pregunta es entonces: ¿hay una mejor manera?
Estoy de acuerdo con otras soluciones que no sean las de Procedimiento almacenado, ya que me doy cuenta de que puede que no sea el camino a seguir. Preferiblemente, me gustaría saber si alguien puede hacerlo mejor dentro del Procedimiento almacenado, pero si no es así, ¿cómo se maneja permitiendo que el usuario ordene dinámicamente tablas de datos (también bidireccionalmente) con ASP.NET?
¡Y gracias por leer (o al menos descremar) una pregunta tan larga!
PD: Alégrate de no haber mostrado mi ejemplo de un procedimiento almacenado que admite la ordenación dinámica, el filtrado dinámico / la búsqueda de texto de columnas, la paginación a través de ROWNUMBER () OVER, y prueba ... atrapar con la reversión de transacciones en errores ... "tamaño gigante" ni siquiera comienza a describirlos.
Actualizar:
- Me gustaría evitar el SQL dinámico . Analizar una cadena y ejecutar un EXEC en ella anula mucho el propósito de tener un procedimiento almacenado en primer lugar. Sin embargo, a veces me pregunto si los inconvenientes de hacer algo así no valdrían la pena, al menos en estos casos especiales de clasificación dinámica. Aún así, siempre me siento sucio cada vez que hago cadenas SQL dinámicas como esa, como si aún viviera en el mundo ASP clásico.
- Gran parte de la razón por la que queremos procedimientos almacenados en primer lugar es por seguridad . No puedo hacer una llamada por cuestiones de seguridad, solo sugerir soluciones. Con SQL Server 2005 podemos establecer permisos (por usuario si es necesario) a nivel de esquema en procedimientos almacenados individuales y luego denegar cualquier consulta directamente en las tablas. Criticar los pros y los contras de este enfoque es quizás para otra pregunta, pero nuevamente no es mi decisión. Solo soy el mono código principal. :)