Редакция от 28 ноября 2010 года
или я что-то не так делаю?
Да.Вам известны два простых набора, которые вы знаете, и вы сосредоточены на создании третьего набора из этих двух наборов.
Использование реляционного подхода (реляционная модель; код и дата)к проблеме означает думать это с точки зрения множеств.Оба ваших исходных набора являются проекциями данных в базе данных.Поэтому вместо того, чтобы объединять их, результаты, как есть, в третий набор, вам нужно думать только о новом наборе, о новой проекции непосредственно из данных в базе данных.
Во-вторых, после того, как вы это сделаете, легко перейти к прямому подходу к языку SQL, чтобы получить результат.Если (1) понят, требуемое программирование - простая логическая проблема.Конечно, стандарт SQL ISO / IEC / ANSI разрешает различные конструкции кода, и есть много способов получить один и тот же результат;я говорю о логике программы;простота;эффективность;а не нет.
Это будет выполняться быстрее на любой платформе, совместимой с ISO / IEC / ANSI SQL.
- Общеизвестно, что Oracle несовместим или сильно расширяет определения стандартов, чтобы казаться совместимыми, поэтому я принимаю, что этот код может не выполняться быстрее в Oracle,В частности, люди из Oracle, кажется, знают, что скалярные подзапросы (которые здесь требуют логики и простоты) не работают должным образом, и вместо этого они используют другие конструкции.
- Любая такая альтернативная конструкция является отклонением от (1) и (2).).(Например, ответ Тони Эндрюса буквально принимает вопрос ОП, использует два известных набора; объединяет их; передает их в 3-й набор; затем извлекает из этого 3 столбца.)
Попробуйте это, это ориентированный на наборы метод манипулирования реляционными базами данных, производящий новый отдельный набор непосредственно из данных (1), независимо от двух известных наборов.Затем он использует язык SQL простым, логичным способом (2), чтобы получить это требование.Да, это два скалярных подзапроса (возвращает одно значение):
<code>SELECT Producers.name Prod,
(SELECT COUNT(Animals.idanimal)
FROM Animals,
AnimalsBought
WHERE p.nif = Animals.owner
AND Animals.idanimal = AnimalsBought.idanimal
) AnimalsBought,
(SELECT COUNT(Animals.idanimal)
FROM Animals,
AnimalsExploration
WHERE p.nif = Animals.owner
AND Animals.idanimal = AnimalsExploration.idanimal
) AnimalsExploration
FROM Producers p
Сравнение 29 ноября 2010 г.
Этот раздел актуален только для людей, интересующихсяВ тестировании участвовали мы с Тони и их выводы
Меня беспокоило, что Тони сообщил, что Oracle выполняет свой запрос за 2 секунды против моего запроса за 5 секунд.Я не мог смириться с мыслью, что код вдвое длиннее;который использует в 3 раза больше наборов, может работать быстрее.
Я провел тест на Sybase.У меня уже есть доступная структура бенчмарков, которая позволила бы тестировать очень похожий код (тест на моих таблицах не должен был объединять две таблицы, но я включил его, чтобы он был как можно ближе к вопросу OP).2.0M CustomerTransactions (Animals) распространяется на 100 клиентов (производителей).То есть оба запроса выдают один и тот же набор результатов (100 клиентов (производителей) по 3 столбцам) из 2,0 млн точек данных или 100 х 20000 скаляров.Давайте точно определим их, чтобы мы могли лучше понять и сравнить их.Сначала используется DDL для используемых структур:
Ссылка на DDL
1_Set_Scalar_Subquery
Точныйэквивалент моего кода, приведенного выше, для доступных структур бенчмарка:
- Из описания OP, структура требования (1) представляет собой список клиентов (производителей).
- Это легко перевести (2) в простой запрос:
SELECT ... FROM Customer (Producer)
- Далее нам понадобятся еще два столбца, специфические вещи (1) re Customer (Producer), которые представляют собой данные, которые могут бытьполучено (не доступно в виде физических столбцов) из базы данных
- Это легко сделать (2) с помощью двух скалярных запросов подзапросов
Ссылка на 1_Set_Scalar_Subquery Code &Синхронизация
1083, 1073, 1103, 1073, 1080 мс
Статистика Sybase показывает 3 сканирования:
Клиент (Производитель) сканируется один раз
CustomerTransaction (Животное) сканируется дважды (один раз для каждого скалярного подзапроса)
3_Set_Inline_Query
Точный эквивалент кода Тони, отображаемый для доступных структур эталонных тестов:
Ссылка на 3_Set_Inline_Query Code & Timings
1820, 1850, 1846, 1843, 1850 мс
То есть На 70% медленнее .
Статистика Sybase показывает 9 сканирований:
Клиент (производитель) сканируется дважды (внешний запрос) CustomerTransaction (животное) сканируется дважды (2 встроенных набора)
Рабочий стол 2 сканируется 3раз (объединенный набор)
Рабочие таблицы 1 и 3 сканируются по одному разу (для GROUP BYs
)
Конечно, 70% разница будет преувеличена на занятом сервере.
3_Set_Inline_Query_Improved
Поскольку исходные наборы, предоставленные OP, были неэффективными, т.е.это может быть легко улучшено, 2 x GROUP BYs
, которые требуют рабочих столов, могут быть удалены, и т.д .;чтобы создать что-то более разумное, оставаясь встроенным, по сравнению со скалярным подзапросом, я это сделал.Конечно, это означает, что вместо манипулирования большим объединенным набором, а затем GROUP BY
для получения агрегатов;использовать (1) и (2) для манипулирования только требуемым набором и заполнять агрегаты с помощью скалярных подзапросов.
Целью этого было проверить достоверность моего утверждения о том, что Sybase выполняет как скалярные подзапросы, так и встроенныезапросы с той же эффективностью.По крайней мере, теперь мы сравниваем два хороших сегмента кода, каждый из которых имеет совершенно разную структуру, вместо одного хорошего сегмента кода с одним плохим сегментом кода.
Ссылка на 3_Set_Inline_Query_Improved Code & Timings
1103, 1073, 1103, 1073, 1080 мс
Это практически идентично 1_Set_Scalar_Subquery
Статистика Sybase показывает 4 сканирования:
Клиент (Производитель) сканируется один раз
CustomerTransaction (Животное) сканируется дважды (один раз для каждого скалярного подзапроса; даже при наличии 2 встроенных наборов)
Клиент (Производитель) сканируется один разmore (для самого внешнего запроса)
Поэтому (в этом простом тесте) доказано, что Sybase выполняет встроенные запросы точно так же, как и скалярные подзапросы.Учитывая, что в данном случае это 3 набора против 1 набора, я бы сказал, что Оптимизатор проделал большую работу в Нормализации запроса (не сглаживая, как спросил Тони, и я ответил), чтобыпроизводить идентичные результаты производительности.
Другая вещь, доказанная этим упражнением, заключается в важности подхода (1), (2).Предполагая даже разумную нормализацию, к данным в реляционной базе данных лучше всего подходить с помощью реляционного, ориентированного на множество подхода.Это приводит к более простому и эффективному коду.Конечно, меньший, более простой, нормализованный код работает лучше.
За исключением Oracle, где из-за его особенностей и ограничений люди должны избегать этого и использовать конструкции, которые, как известно, не работают плохо.
Сравнение 01 декабря 2010
Тони опубликовал результаты своих тестов.Они говорят сами за себя, дело закрыто.
Однако он представил проблему, заключающуюся в том, что, хотя в данном конкретном случае (этот вопрос) Oracle выполняет 1_Set_Scalar_Subquery почти в два раза быстрее, чем 3_Set_Inline_Query, поскольку COUNT () обслуживается из индекса без доступа к таблице, что в общем случае для Oracleвыполняет встроенные запросы намного лучше, чем скалярные подзапросы, и протестировал SUM (), чтобы изучить эффект посещения таблиц.
Поэтому я выполнил тот же тест в Sybase, изменив COUNT () на SUM ().Как и в Oracle, COUNT () был закрытым запросом, но теперь SUM () требует посещения таблиц.
(Выводы позже, когда Тони публикует некоторые недостающие деталиМеня беспокоит то, что Sybase обрабатывает 2,0 млн строк менее чем за 2 секунды, а Oracle, даже в лучшем случае, не может справиться с той же работой со 1000 ++ строками за 6 секунд (ожидается заполнение таблиц)).)
Тони опубликовал некоторые недостающие детали, но также открывает новые проблемы, которые я указал в моих комментариях к его сообщению.Поэтому было бы немного преждевременно делать окончательные выводы
Пока что в аналогичных тестах (недостаточно близко для меня) на аналогичных системах (нормально, система Sybase имеет 2 дополнительных/ w слоев и не является сервером):
Sybase работает со строками COUNT 4.0M за 1.1 и 1.5 секунды ;
Oracle выполняет аналогичную работу в 3,5 и 5,7 секунд .
Sybase выполняет SUM за 2,1 и 3,0 с ;
Oracle не завершил подзапрос и выполнил инлайн в 79 секунд
(ожидается запрос дополнительной информации).
В лучшем случае (подзапрос не выполнен, поэтому мы не можем это подсчитать), используя плохой кодкак требуется для Oracle, Oracle в 26 раз медленнее, чем Sybase .
И в 40 раз медленнее, когда кодер Sybase использовал теорию реляционных множеств.
1_Set_Scalar_Subquery_SUM
То же, что и 1_Set_Scalar_Subquery_SUM, с COUNT (), измененным на SUM ().
Ссылка на 1_Set_Scalar_Subquery_SUM Code & Timings
2173, 2153, 2163, 2153, 2173 мс
Статистика Sybase показывает 3 сканирования:
Заказчик (Производитель) сканируется один раз
CustomerTransaction (Animal)сканируется дважды (один раз для каждого скалярного подзапроса)
3_Set_Inline_Query_SUM
То же, что 3_Set_Inline_Query, с COUNT (), измененным на SUM ().
Ссылка на код 3_Set_Inline_Query_SUM и время
3033, 2993, 3033, 2993, 3013 мс
То есть 38% медленнее .
Статистика Sybase показывает 9 сканирований:
Клиент (производитель) сканируется дважды (внешний запрос) CustomerTransaction (животное) сканируется дважды (2 встроенных набора)
Рабочий стол 2 сканируется 3раз (объединенный набор)
Рабочие таблицы 1 и 3 сканируются по одному разу (для GROUP BYs
)
Конечно, разница в 38% будет преувеличена на занятом сервере.
3_Set_Inline_Query_Improved_SUM
То же, что 3_Set_Inline_Query_Improved, с COUNT () изменено на SUM ().
Ссылка на 3_Set_Imp_Inline_Inline
2183, 2180, 2183, 2143, 2143 мс
Это практически идентично 1_Set_Scalar_Subquery
Статистика Sybase показывает 4 сканирования:
Клиент (Производитель) сканируется один раз
CustomerTransaction (Animal) сканируется дважды (один раз для каждого скалярного подзапроса; даже при наличии 2 встроенных наборов)
Клиент (производитель) сканируется еще раз (для внешней query)
Одна вещь в контексте Sybase.Мое утверждение о том, что Sybase Optimizer нормализует запросы и, следовательно, обрабатывает скалярные подзапросы, а также встроенные представления, еще раз доказано.Плохой код (3_Set_Inline_Query не улучшен ) работает медленнее, но только пропорционально медленнее, без существенной разницы.