Пересмотрено 15 января 11
Я уверен, что где-то есть простой способ!
Да, есть. Но сначала два вопроса.
Таблица не является таблицей реляционной базы данных. Он не имеет уникального ключа, который требуется RM и нормализацией (в частности, что каждая строка должна иметь уникальный идентификатор; не обязательно PK). Поэтому SQL, стандартный язык для работы с таблицами реляционной базы данных, не может выполнять над ним основные операции.
- это куча (структура данных, вставленная и удаленная в хронологическом порядке), с записями, а не строками.
- все операции с использованием SQL будут ужасно медленными и не будут правильными
- УСТАНОВИТЕ ROWCOUNT в 1, выполните обработку строк, и SQL будет нормально работать с кучей
- Ваша лучшая ставка - использовать любой юникс для работы с ним (awk, cut, chop). Они невероятно быстрые. Сценарий awk, необходимый для выполнения вашего требования, занял бы 3 минуты, и он будет выполняться за секунды для миллионов записей (я написал несколько на прошлой неделе).
,
Таким образом, вопрос на самом деле SQL, чтобы найти первое вхождение наборов данных в нереляционную кучу .
Теперь, если ваш вопрос был SQL, чтобы найти первое вхождение наборов данных в реляционную таблицу , подразумевающее, конечно, некоторый уникальный идентификатор строки, это было бы (а) легко в SQL и (б) ) быстро в любой разновидности SQL ...
- кроме Oracle, который, как известно, плохо обрабатывает подзапросы (в частности, комментарии Тони Эндрюса, он является известным авторитетом в Oracle). В этом случае используйте материализованные представления.
,
Вопрос очень общий (без жалоб). Но многие из этих конкретных потребностей обычно применяются в более широком контексте, и у контекста есть требования, которые здесь отсутствуют в спецификации. Обычно требуется простой подзапрос (но в Oracle используется материализованное представление, чтобы избежать подзапроса). И подзапрос тоже зависит от внешнего контекста, внешнего запроса. Поэтому ответ на небольшой общий вопрос не будет содержать ответа на конкретную конкретную потребность.
Во всяком случае, я не хочу избегать вопроса. Почему бы нам не использовать пример из реального мира, а не простой общий пример; и найти первое или последнее вхождение, или минимальное, или максимальное значение, набора данных в другом наборе данных в реляционной таблице ?
Основной запрос
Давайте используем ▶ Модель данных 10 из вашего предыдущего вопроса.
Сообщать обо всех Alerts
с определенной даты, с максимальным значением для продолжительности, которые не являются Acknowledged
Поскольку вы будете использовать абсолютно одинаковую технику (с разными именами таблиц и столбцов) для всех ваших временных требований и требований к истории, вам необходимо полностью понять базовую конструкцию подзапроса и его различные приложения.
Введение
Обратите внимание, что у вас есть не только чистая база данных 5NF с реляционными идентификаторами (составные ключи), у вас есть полная временная возможность во всем, и временное требование отображается без нарушения 5NF (без обновлений), что означает ValidToDateTime
для периодов и длительностей выводится, а не дублируется в данных. Дело в том, что все усложняется, поэтому не лучший пример учебника по подзапросам .
- Помните, что движок SQL - это процессор множеств, поэтому мы подходим к проблеме с мышлением, ориентированным на множество
- не заглушайте двигатель до обработки строк; очень медленно
- и, что более важно, не нужно
- Подзапросы - это обычный SQL. Синтаксис, который я использую, является прямым ISO / IEC / ANSI SQL.
- если вы не можете кодировать подзапросы в SQL, вы будете очень ограничены; а затем необходимо ввести дублирование данных или использовать большие результирующие наборы в качестве материализованных представлений или временных таблиц или всевозможных дополнительных данных и дополнительной обработки, которые будут медленными до очень медленными , а не упоминание совершенно не нужно
- если есть что-то, что вы не можете сделать в по-настоящему реляционной базе данных (и мои Модели данных всегда таковы), не переключаясь на обработку строк или встроенные представления или временные таблицы, обратитесь за помощью, что вы и сделали.
- Вам нужно полностью понять первый подзапрос (проще), прежде чем пытаться понять второй; и т.д.
Метод
Сначала создайте запрос Outer с использованием минимальных объединений и т. Д. На основе структуры нужного вам набора результатов и ничего более. Очень важно, чтобы структура внешнего запроса была разрешена первой; в противном случае вы будете пытаться привести подзапрос в соответствие с внешним запросом, и наоборот.
- Это также требует подзапроса. Так что оставьте эту часть на время, а поднимите ее позже. На данный момент внешний запрос получает все (не подтвержденные)
Alerts
после определенной даты
Требуемый ▶ код SQL ◀ необходим на странице 1 (извините, возможности редактирования SO ужасны, он уничтожает форматирование, а код уже отформатирован).
Затем создайте подзапрос, чтобы заполнить каждую ячейку.
Подзапрос (1) Производное Alert.Value
Это простой производный элемент данных, выберите Value
из Reading
, который сгенерировал Alert
. Таблицы связаны, количество элементов 1 :: 1, поэтому это прямое соединение на ПК.
- Тип подзапроса, требуемый здесь: Коррелированный подзапрос , нам нужно сопоставить таблицу во внешнем запросе с таблицей во (внутреннем) подзапросе.
- для этого нам нужен псевдоним для таблицы в запросе Outer, чтобы сопоставить его с таблицей в подзапросе.
- чтобы провести различие, я использовал псевдонимы только для такой требуемой корреляции и полностью квалифицированные имена для простых объединений
- Подзапросы работают очень быстро в любом движке (кроме Oracle)
- SQL - громоздкий язык. Но это все, что у нас есть. Так что привыкни.
▶ Код SQL ◀ , необходимый на стр. 2.
Я специально дал вам сочетание объединений во Внешнем Запросе с получением данных через Подзапрос, чтобы вы могли учиться (вы можете поочередно получить Alert.Value
через объединение, но это будет еще более громоздким ).
Следующий подзапрос нам нужен Alert.PeakValue
. Для этого нам нужно определить временную продолжительность Alert
. У нас есть начало Alert
Duration; нам нужно определить конец Duration, который является следующим (по времени) Reading.Value
, то есть в пределах диапазона . Это также требует подзапроса, с которым нам лучше разобраться в первую очередь.
- Работа логики изнутри, наружу. Старый добрый БОДМАС.
Подзапрос (2) Производная Alert.EndDtm
Несколько более сложный запрос для выбора первого Reading.ReadingDtm
, который больше или равен Alert.ReadingDtm
, который имеет Reading.Value
, который меньше или равен Sensor.UpperLimit
.
Обработка временных данных 5NF
Для обработки временных требований в базе данных 5NF (в которой EndDateTime
хранится , а не , как дубликаты данных), мы работаем только с StartDateTime
, а EndDateTime
- производное : это следующий StartDateTime
. Это временное понятие Продолжительность .
- Технически, это на один миллисек (независимо от используемого разрешения для типа данных) меньше.
- Однако, чтобы быть разумным, мы можем говорить и сообщать,
EndDateTime
как просто Next.StartDateTime
, и игнорировать одну миллисекундную проблему.
- ThКод всегда должен использовать > =
This.StartDateTime
и <</strong> Next.StartDateTime
. Это устраняет множество ошибок, которых можно избежать - Обратите внимание, что эти операторы сравнения, которые ограничивают временную длительность и должны использоваться обычным образом повсеместно, как описано выше, совершенно не зависят от аналогичного сравнения.операторы, связанные с бизнес-логикой, например.
Sensor.UpperLimit
(т. Е. Следите за этим, потому что оба часто находятся в одном предложении WHERE
, и их легко перепутать или запутаться).
Требуемый ▶ код SQL ◀ вместе с используемыми тестовыми данными приведен на стр. 3.
Подзапрос (3) Derive Alert.PeakValue
Теперь это легко.Выберите MAX(Value)
из Readings
между Alert.ReadingDtm
и Alert.EndDtm
, продолжительность Alert
.
▶ SQL-код ◀ требуетсяна стр. 4.
Скалярный подзапрос
Помимо коррелированных подзапросов, все перечисленные выше Скалярные подзапросы , так как они возвращают одинзначение;каждая ячейка в сетке может быть заполнена только одним значением.(Нескалярные подзапросы, которые возвращают несколько значений, вполне допустимы, но не для вышеуказанного.)
Подзапрос (4) Подтвержденные оповещения
Хорошо, теперь, когдау вас есть дескриптор вышеупомянутых коррелированных скалярных подзапросов, те, которые заполняют ячейки в наборе, набор, который определяется внешним запросом, давайте посмотрим на подзапрос, который можно использовать для ограничения внешнего запроса.Нам не нужны все Alerts
(выше), мы хотим Un-Acknowledged Alerts
: идентификаторы, которые существуют в Alert
, которые не существуют в Acknowledgement
.Это не заполнение ячеек, это изменение содержимого внешнего набора.Конечно, это означает изменение предложения WHERE
.
- Мы не изменяем структуру Внешнего набора, поэтому никаких изменений в
FROM
и * нет.1287 * существующие WHERE
пункты.
Просто добавьте условие WHERE
, чтобы исключить набор Acknowledged Alerts
.1 :: 1 кардинальное число, прямое коррелированное соединение.
Код ▶ SQL ◀ требуется на странице 5.
Разница в том, что нескалярный подзапрос , производящий набор строк (один столбец).У нас есть весь набор Alerts
(Внешний набор), сопоставленный со всем набором Acknowledgements
.
- Соответствие обработано, потому что мы сказали движку, что подзапрос коррелирован , используя псевдоним (не нужно идентифицировать громоздкие объединения)
- Используйте
1
, потому что мы выполняем проверку существования.Визуализируйте его как столбец, добавленный к набору Alert
, определенному запросом Outer. - Никогда не используйте *, потому что нам не нужен весь набор столбцов, и это будет медленнее
- АналогичноЕсли не использовать корреляцию, это означает, что требуется
WHERE NOT IN ()
, но опять-таки, который создает определенный набор столбцов, а затем сравнивает два набора.Гораздо медленнее.
Подзапрос (5) Actioned Alerts
В качестве альтернативного ограничения на внешний запрос для неактивных Alerts
вместо(4), исключить набор Actioned Alerts
.Прямое коррелированное соединение.
Требуемый ▶ SQL-код SQL необходим на стр. 5.
Этот код был протестирован на Sybase ASE 15.0.3используя 1000 Alerts
и 200 Acknowledgements
различных комбинаций;и Readings
и Alerts
, указанные в документе.Нулевое время выполнения в миллисекундах (разрешение 0,003 секунды) для всех выполнений.
Если вам это нужно, вот код ▶ SQL в текстовом формате ◀ .
Ответ на комментарии
(6) ▶ Зарегистрировать предупреждение от чтения ◀
Этот код выполняется в цикле (прилагается), выбирая новый Readings
которые находятся за пределами диапазона и создают Alerts
, за исключением случаев, когда это применимо Alerts
уже существует.
(7) ▶ Загрузка оповещения из чтения From
Учитывая, что у вас есть полный сКроме данных испытаний для Reading
, этот код использует измененную форму (6) для загрузки применимого Alerts
.
Общая проблема
Это "просто", когда ты умеешь. Я повторяю, написание SQL без возможности писать подзапросы очень ограничивает; он необходим для работы с реляционными базами данных, для чего и был разработан SQL.
- Половина причины, по которой разработчики внедряют ненормализованные кучи данных (массовое дублирование данных), заключается в том, что они не могут писать подзапросы, необходимые для нормализованных структур
- дело не в том, что они "денормализованы для исполнения"; это то, что они не могут кодировать для Normalized. Я видел это сто раз.
- Показательный пример: у вас есть полностью нормализованная реляционная база данных, и ее сложность заключается в ее кодировании, и вы рассматривали дублирование таблиц для целей обработки.
- И это не считая дополнительной сложности временной базы данных; или временная база данных 5NF.
- Нормализация означает Никогда ничего не дублировать , более позднее известное как Не повторяйте себя
- Мастер Suqueries и вы будете в 98-м процентиле: Нормализованные, истинные реляционные базы данных; нулевое дублирование данных; очень высокая производительность.
Я думаю, вы можете выяснить оставшиеся у вас запросы.
реляционный идентификатор
Обратите внимание, что этот пример также демонстрирует силу использования реляционных идентификаторов , поскольку не нужно объединять несколько таблиц между теми, которые мы хотим (да! Истина - это реляционные идентификаторы присоединяется меньше, не больше, чем Id
). Просто следуйте сплошным линиям.
- Ваше временное требование требует ключи, содержащие
DateTime
. Представьте, что вы пытаетесь закодировать вышеприведенное с помощью Id
PK, будет два уровня обработки: один для объединений (и их будет гораздо больше), а другой для обработки данных.
Метка
Я стараюсь держаться подальше от разговорных ярлыков («вложенные», «внутренние» и т. Д.), Потому что они не являются конкретными, и придерживаются определенных технических терминов. Для полноты и понимания:
- Подзапрос после предложения
FROM
представляет собой Материализованное представление , набор результатов, полученный в одном запросе и затем переданный в предложение FROM
другого запроса в виде «таблицы».
- Типы Oracle называют это встроенным представлением.
- В большинстве случаев вы можете написать коррелированные подзапросы в виде материализованных представлений, но это значительно больше операций ввода-вывода и обработки (поскольку обработка подзапросов в Oracles является пропастью, только для Oracle материализованные представления "быстрее").
,
Подзапросом в предложении WHERE
является Предикатный подзапрос , поскольку он изменяет содержимое результирующего набора (того, к которому он относится). Может возвращать либо Скалярное (одно значение), либо не Скалярное (много значений).
для скаляров, используйте WHERE column =
или любой скалярный оператор
для не скаляров, используйте WHERE [NOT] EXISTS
или WHERE column [NOT] IN
Suquery в предложении WHERE
не нуждается в для корреляции; следующие работы просто отлично. Определите все лишние придатки: <pre>SELECT [Never] = FirstName,
[Acted] = LastName
FROM User
WHERE UserId NOT IN ( SELECT DISTINCT UserId
FROM Action
)