Pregunta

Estoy buscando refactorizar la consulta a continuación para que sea más legible y modificable. La primera mitad es idéntica a la segunda, con la excepción de la base de datos consultada (sin embargo, los nombres de las tablas son los mismos).

  SELECT
    Column 1 AS c1,
    ...
    Column N AS cN
  FROM
    database1.dbo.Table1

UNION

  SELECT
    'Some String' as c1,
    ...
    NULL as cN
  FROM
    database1.dbo.Table2

UNION

  SELECT
    Column 1 AS c1,
    ...
    Column N AS cN
  FROM
    database2.dbo.Table1

UNION

  SELECT
    'Some String' as c1,
    ...
    NULL as cN
  FROM
    database2.dbo.Table2

Esta consulta es la definición de DRY y me está llamando para que vuelva escrito, pero no tengo idea de cómo!

EDITAR : No podemos usar linq y deseamos resultados distintos; Estoy tratando de hacer que la consulta sea más pequeña en tamaño de archivo físico, no en resultados devueltos.

EDITAR : La base de datos que estoy consultando es una base de datos de ERP patentada. Reestructurarlo no es una opción.

¿Fue útil?

Solución

Este es un patrón SQL bastante estándar. A veces es fácil transferir de forma desaconsejada los principios del código de procedimiento / OOP como DRY a SQL, pero no son necesariamente conceptos transferibles.

Observe la facilidad con la que puede asimilar todo el diseño lógico de la consulta, frente a la búsqueda a través de submódulos. Si una de las subexpresiones tuviera una columna adicional, o columnas invertidas, sobresaldría. Esta es básicamente una declaración SQL bastante simple para asimilar como una unidad de ejecución, donde su desagregación la confundiría.

Y cuando está depurando, es útil poder usar la opción de resaltado de texto del editor para ejercitar selectivamente partes de la declaración, una técnica que no existe en el código de procedimiento. OTOH, puede ser complicado tratar de rastrear todas las piezas si están dispersas en vistas, etc. Incluso los CTE pueden hacer que esto sea un inconveniente.

Otros consejos

Voy a arriesgarme aquí y decir, en base a la información que nos ha proporcionado;

Eso es tan bueno como va a ser

Un consejo de rendimiento que veo de inmediato es usar UNION ALL en lugar de UNION a menos que desee intencionalmente registros distintos. Un simple <=> eliminará los duplicados, lo que lleva tiempo. <=> no hace eso.

Podría reescribirlo con SQL dinámico y un bucle, pero creo que el resultado sería peor. Si hay suficiente código duplicado para justificar el enfoque sql dinámico, entonces supongo que podría estar justificado.

Alternativamente, ¿ha considerado mover la lógica del procedimiento almacenado a algo como LINQ? Para muchos, esta no es una opción, así que solo pregunto.

Una nota final: resista el impulso de arreglar lo que no está roto solo para que se vea más limpio. Si la limpieza ayudará en el mantenimiento, la verificación, etc., entonces hágalo.

¿Cuál es el problema? ¿Demasiado largo? ¿Demasiado repetitivo?

A veces obtienes SQL feo, no puedes hacer mucho al respecto.

No veo ninguna forma de limpiarlo a menos que desee usar vistas separadas y luego unirlas.

Voto por las vistas, que imponen casi cero gastos generales (OK, tal vez un pequeño costo de tiempo de compilación, pero eso debería ser todo). Entonces tus procesos se convierten en algo de la forma

SELECT * FROM database1.view1
UNION
SELECT * FROM database1.view2
UNION
SELECT * FROM database2.view1
UNION
SELECT * FROM database2.view2

No estoy seguro si quisiera condensarlo más, aunque espero que la mayoría de las plataformas lo toleren.

En el tema Dynamic SQL, aquí hay una muestra, no estoy seguro de si es mejor. El beneficio es que solo tiene que escribir la lista SELECT una vez.

DECLARE @Select1 varchar(1000)
DECLARE @Select2 varchar(1000)

DECLARE @SQL varchar(4000)


SET @Select1 = 'SELECT
    Column 1 AS c1,
    ...
    Column N AS cN'


SET @Select2 = 'SELECT
    ''Some String'' as c1,
    ...
    NULL as cN'


SET @SQL = @Select1 + ' FROM database1.dbo.Table1 '

SET @SQL = @SQL + ' UNION ' + @Select2 + ' FROM database1.dbo.Table2 '

SET @SQL = @SQL + ' UNION ' + @Select1 + ' FROM database2.dbo.Table1 '

SET @SQL = @SQL + ' UNION ' + @Select2 + ' FROM database2.dbo.Table2 '


EXEC @SQL

Si todos sus procesos se ven así, probablemente tenga un problema arquitectónico.

¿Todas sus llamadas a table2 solo tienen un campo útil? (y debido a UNION, ¿terminan teniendo solo una fila?)

Estoy totalmente de acuerdo con la idea de utilizar SQL dinámico parametrizado y / o generación de código para este trabajo, incluso llegando a generar dinámicamente la lista de columnas con INFORMATION_SCHEMA. Esto no es exactamente lo que necesita, pero es un comienzo (puede generar a partir de una tabla de bases de datos y tablas):

DECLARE @template AS varchar(MAX)
SET @template = 'SELECT {@column_list} FROM {@database_name}.dbo.{@table_name}'
DECLARE @column_list AS varchar(MAX)

SELECT @column_list = COALESCE(@column_list + ',', '') + COLUMN_NAME
FROM database1.dbo.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @table_name
ORDER BY ORDINAL_POSITION

DECLARE @sql AS varchar(MAX)
SET @sql = @template
SET @sql = REPLACE(@sql, '{@column_list}', @column_list)
SET @sql = REPLACE(@sql, '{@database_name}', @database_name)
SET @sql = REPLACE(@sql, '{@table_name}', @table_name)

Dependiendo del número de filas devueltas, puede ser mejor usar UNION ALL en las selecciones con una consulta de selección distinta a su alrededor. He visto un problema similar antes y tenía diferentes planes de ejecución para los dos estilos diferentes

SELECT DISTINCT subquery.c1, subquery.cN
FROM
(
SELECT Column 1 AS c1, Column N AS cN FROM database1.dbo.Table1
UNION ALL
SELECT 'Some String' as c1, NULL as cN FROM database1.dbo.Table2
UNION ALL
SELECT Column 1 AS c1, Column N AS cN FROM database2.dbo.Table1
UNION ALL
SELECT 'Some String' as c1, NULL as cN FROM database2.dbo.Table2
) subquery
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top