Есть ли лучший способ отсортировать этот запрос? - PullRequest
2 голосов
/ 16 декабря 2009

Мы генерируем много процедурного SQL, и SQL Server убивает нас. Из-за некоторых проблем, задокументированных в других местах , мы в основном делаем SELECT TOP 2 ** 32 вместо TOP 100 PERCENT.

Примечание: мы должны использовать подзапросы.

Вот наш запрос:

SELECT * FROM ( 
    SELECT [me].*, ROW_NUMBER() OVER( ORDER BY (SELECT(1)) ) 
    AS rno__row__index FROM (
       SELECT [me].[id], [me].[status] FROM (
          SELECT TOP 4294967296 [me].[id], [me].[status] FROM 
          [PurchaseOrders] [me] 
          LEFT JOIN [POLineItems] [line_items] 
          ON [line_items].[id] = [me].[id] 
          WHERE ( [line_items].[part_id] = ? ) 
          ORDER BY [me].[id] ASC
       ) [me]
   ) [me] 
) rno_subq 
WHERE rno__row__index BETWEEN 1 AND 25 

Есть ли лучшие способы сделать это, что каждый может увидеть?

ОБНОВЛЕНИЕ : вот некоторые пояснения по всей проблеме подзапроса:

Ключевое слово моего вопроса - "процедурно". Мне нужна возможность надежно инкапсулировать наборы результатов, чтобы их можно было складывать вместе, как строительные блоки. Например, я хочу получить первые 10 компакт-дисков, упорядоченные по имени исполнителя, который их создал, а также получить соответствующего исполнителя для каждого компакт-диска. Что я делаю, так это собираю монолитный подвыбор, представляющий компакт-диски, упорядоченные по именам соединенных исполнителей, затем примените к нему ограничение, а затем присоедините вложенные вложенные элементы к таблице исполнителей и только затем выполните полученный запрос. Изоляция необходима, потому что код, который запрашивает упорядоченные компакт-диски, не связан и не учитывает код, выбирающий топ-10 компакт-дисков, который, в свою очередь, не связан и не замечает код, запрашивающий соответствующих исполнителей.

Теперь вы можете сказать, что я мог бы переместить внутренний ORDER BY в предложение OVER (), но затем я нарушил инкапсуляцию, так как мне пришлось бы ВЫБРАТЬ столбцы объединяемой таблицы, чтобы я мог заказать их позже. Дополнительной проблемой было бы объединение двух таблиц под одним псевдонимом; если бы в обеих таблицах были столбцы с одинаковыми именами, select me. * остановился бы здесь с неоднозначной ошибкой имени столбца.

Я готов немного пожертвовать производительностью оптимизатора, но 2 ** 32 кажется мне слишком взломанным. Поэтому я ищу золотую середину.

Ответы [ 4 ]

3 голосов
/ 16 декабря 2009
  • Если вы хотите, чтобы верхние строки были у me.id, просто попросите об этом в ORDER BY в ROW_NUMBER. Не гонитесь за подзапросами и TOP.
  • Если у вас есть предложение WHERE в поле объединенной таблицы, у вас может быть внешнее JOIN. Все внешние поля будут NULL и отфильтрованы WHERE, так что, по сути, это внутреннее соединение.

.

WITH cteRowNumbered AS (
  SELECT [me].id, [me].status 
  ROW_NUMBER() OVER (ORDER BY me.id ASC) AS rno__row__index 
  FROM [PurchaseOrders] [me]           
  JOIN [POLineItems] [line_items] ON [line_items].[id] = [me].[id]          
  WHERE [line_items].[part_id] = ?)
SELECT me.id, me.status 
FROM cteRowNumbered
WHERE rno__row__index BETWEEN 1 and 25

Я использую CTE вместо подзапросов только потому, что считаю их более читабельными.

1 голос
/ 17 декабря 2009

Престижность единственному человеку, который просматривал сговор и фактически попробовал запрос к большой таблице, к которой у нас нет доступа. Всем остальным говорят, что это просто не сработает (вернут случайные строки) - мы знаем, что написано в руководстве, и знаем, что это взлом - вот почему мы задаем вопрос в первую очередь. Однако откровенно отклонить запрос, даже не попробовав его, довольно поверхностно. Может ли кто-нибудь предоставить нам реальный пример (с предыдущими инструкциями CREATE / INSERT), демонстрирующий неправильную работу вышеуказанного запроса?

1 голос
/ 16 декабря 2009

Использование:

SELECT x.*
  FROM (SELECT po.id, 
               po.status,
               ROW_NUMBER() OVER( ORDER BY po.id) AS rno__row__index
          FROM [PurchaseOrders] po
          JOIN [POLineItems] li ON li.id = po.id
         WHERE li.pat_id = ?) x
 WHERE x.rno__row__index BETWEEN 1 AND 25
ORDER BY po.id ASC

Если вы не указали детали, чтобы упростить пример, вам не понадобятся все ваши подзапросы в том, что вы предоставили.

0 голосов
/ 16 декабря 2009

Ваше обновление делает вещи намного понятнее. Я думаю, что подход, который вы используете, серьезно ошибочен. Хотя приятно иметь в своих приложениях инкапсулированный, многократно используемый код, интерфейсные приложения сильно отличаются от базы данных. Они обычно имеют дело с небольшими структурами и небольшими дискретными процессами, которые работают против этих структур. С другой стороны, базы данных часто имеют дело с таблицами, которые измеряются в миллионах строк, а иногда и больше. Использование одних и тех же методологий часто приводит к тому, что код просто работает так плохо, что его невозможно использовать. Даже если он работает сейчас, вполне вероятно, что он не будет масштабироваться и вызовет серьезные проблемы в будущем.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...