Как бы вы написали этот запрос? - PullRequest
1 голос
/ 29 января 2009

Я хочу изменить приведенный ниже запрос на что-то более читаемое и изменяемое. Первая половина идентична второй, за исключением того, что база данных запрашивается из (хотя имена таблиц совпадают.)

  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

Этот запрос является определением DRY и призывает меня переписать, но я понятия не имею, как!

РЕДАКТИРОВАТЬ : Мы не можем использовать linq, и мы желаем отличных результатов; Я пытаюсь уменьшить размер запроса по физическому размеру файла, а не по результатам.

РЕДАКТИРОВАТЬ : База данных, к которой я обращаюсь, является частной базой данных ERP. Реструктуризация не возможна.

Ответы [ 8 ]

3 голосов
/ 29 января 2009

Я собираюсь выйти здесь на конечность и сказать, основываясь на информации, которую вы нам дали;

Это так же хорошо, как и получить

2 голосов
/ 29 января 2009

Это довольно стандартный шаблон SQL. Иногда легко незаметно перенести принципы ООП / процедурного кода, такие как DRY, в SQL, но они не обязательно являются переносимыми понятиями.

Обратите внимание, как легко вы можете получить весь логический дизайн запроса по сравнению с поиском подмодулей. Если у одного из подвыражений есть дополнительный столбец или столбцы перевернуты, он выпадет. По сути, это довольно простой SQL-оператор, который превращается в исполнительную единицу, где его дезагрегация может привести к путанице.

А когда вы отлаживаете, удобно использовать возможность подсветки текста редактора, чтобы выборочно использовать части выражения - метод, которого нет в процедурном коде. ОТО, может быть грязно пытаться отследить все части, если они разбросаны по видам и т. Д. Даже CTE могут сделать это неудобным.

2 голосов
/ 29 января 2009

Один совет по производительности, который я вижу вне очереди, использует UNION ALL вместо UNION, если вы намеренно не хотите отличных записей. Простое UNION удалит дубликаты, что занимает много времени. UNION ALL не делает этого.

Вы можете переписать его с помощью динамического SQL и цикла, но я думаю, что результат будет хуже. Если дублирующего кода достаточно, чтобы оправдать динамический SQL-подход, то я думаю, что он может быть оправдан.

В качестве альтернативы, вы рассматривали возможность перемещения логики из хранимой процедуры во что-то вроде LINQ? Для многих это не вариант, поэтому я просто спрашиваю.

Последнее замечание: не поддавайтесь желанию починить то, что не сломано, только чтобы оно выглядело чище. Если очистка поможет в техническом обслуживании, проверке и т. Д., То сделайте это.

1 голос
/ 29 января 2009

Я голосую за представления, которые накладывают почти нулевые накладные расходы (хорошо, возможно, небольшие затраты времени компиляции, но это должно быть все). Тогда ваши процы станут чем-то вроде

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

Я не уверен, захочу ли я еще сократить это, хотя я ожидаю, что большинство платформ это допустит.

1 голос
/ 29 января 2009

В чем проблема? Слишком долго? Слишком повторяющийся?

Иногда вы получаете ужасный SQL - мало что можете с этим поделать.

Я не вижу способа очистить его, если вы не хотите использовать отдельные представления, а затем объединить их вместе.

0 голосов
/ 29 января 2009

В зависимости от количества возвращаемых строк, вам лучше всего использовать UNION ALL для выбора с выделенным запросом вокруг него. Я видел подобную проблему раньше и имел разные планы выполнения для двух разных стилей

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
0 голосов
/ 29 января 2009

Если все ваши процы выглядят так - возможно, у вас архитектурная проблема.

Все ли ваши вызовы в table2 содержат только одно полезное поле? (а из-за UNION в итоге будет только один ряд?)

Я полностью поддерживаю идею использования параметризованного динамического SQL и / или генерации кода для этой работы, даже до того, что динамически генерирую список столбцов, используя INFORMATION_SCHEMA. Это не совсем то, что вам нужно, но это начало (вы можете создать таблицу баз данных и таблиц):

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)
0 голосов
/ 29 января 2009

На тему динамического SQL - вот пример - не уверен, что это лучше. Преимущество заключается в том, что вы должны написать список SELECT только один раз.

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