SQL для поиска первого вхождения наборов данных в таблицу

скажите, если у меня есть стол:

CREATE TABLE T
(
    TableDTM  TIMESTAMP  NOT NULL,
    Code      INT        NOT NULL
);

и я вставляю несколько строк:

INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:00:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:10:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:20:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:30:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:40:00', 0);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:50:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:00:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:10:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:20:00', 0);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:30:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:40:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:50:00', 3);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 12:00:00', 3);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 12:10:00', 3);

таким образом, я получаю таблицу, похожую на:

2011-01-13 10:00:00, 5
2011-01-13 10:10:00, 5
2011-01-13 10:20:00, 5
2011-01-13 10:30:00, 5
2011-01-13 10:40:00, 0
2011-01-13 10:50:00, 1
2011-01-13 11:00:00, 1
2011-01-13 11:10:00, 1
2011-01-13 11:20:00, 0
2011-01-13 11:30:00, 5
2011-01-13 11:40:00, 5
2011-01-13 11:50:00, 3
2011-01-13 12:00:00, 3
2011-01-13 12:10:00, 3

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

2011-01-13 10:00:00, 5
2011-01-13 10:40:00, 0
2011-01-13 10:50:00, 1
2011-01-13 11:20:00, 0
2011-01-13 11:30:00, 5
2011-01-13 11:50:00, 3

я возился с подзапросами и тому подобное большую часть дня, и по какой-то причине я не могу его взломать. Я уверен, что где-то есть простой способ!

Я, вероятно, хотел бы исключите 0 из результатов, но сейчас это не важно..

4 ответов


Исправлено 15 Янв 11

я уверен, что есть простой способ где-то!

Да, есть. Но сначала два вопроса.

  1. таблица не является таблицей реляционной базы данных. Он не имеет уникального ключа, который требуется RM и нормализацией (в частности, что каждая строка должна иметь уникальный идентификатор; не обязательно ПК). Следовательно, SQL, стандартный язык для работы с реляционными Таблицы базы данных, не может выполнять основные операции над ним.

    • это куча (структура данных, inserted и deleted в хронологическом порядке), с учета не строк.
    • все операции с использованием SQL будут ужасно медленными и не будут правильными
    • установите ROWCOUNT в 1, Выполните обработку строк, и SQL будет работать на куче просто отлично
    • лучше всего использовать любой unix utiliy для работы с ним (awk, cut, chop). Они молниеносно. Сценарий awk, необходимый для ответа на ваше требование, займет 3 минуты, чтобы написать, и он будет работать в секундах для миллионов записей (я написал несколько на прошлой неделе).
      .

    так что вопрос действительно SQL, чтобы найти первое появление наборов данных в нереляционной куче.

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

    • кроме Oracle, который известен обрабатывать подзапросы плохо (в частности, комментарии Тони Эндрюса, он является известным авторитетом в Oracle). В этом случае используйте материализованные представления.
      .
  2. вопрос очень общий (без жалобы). Но многие из этих конкретных потребностей обычно применяются в более широком контексте, и контекст имеет требования, которые здесь отсутствуют в спецификации. Как правило, требуется простой подзапрос (но в Oracle используйте материализованное представление, чтобы избежать подзапроса). И подзапрос, тоже зависит от внешнего контекста, внешнего запроса. Поэтому ответ на небольшой общий вопрос не будет содержать ответа на фактическую конкретную потребность.


во всяком случае, я не хочу уклоняться от вопроса. Почему бы нам не использовать реальный мир пример, а не простой общий; и найдите первое или последнее вхождение или минимальное или максимальное значение набора данных в другом наборе данных в реляционной таблице ?

Основной Запрос

давайте использовать ▶Данные Модели◀ из вашего предыдущего вопроса.

отчет все Alerts С определенной даты, с пиковым значением для продолжительности, которые не являются Acknowledged

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

введение

обратите внимание, что у вас есть не только чистая база данных 5NF с реляционными идентификаторами (составными ключами), у вас есть полная временная возможность повсюду, и временная требование отображается без нарушения 5NF (без аномалий обновления), что означает ValidToDateTime для периодов и длительностей выводится, а не дублируется в данных. Дело в том, что это усложняет вещи, следовательно, это не лучший пример для учебника по Подзапросам.

  • помните, что SQL engine-это set-процессор, поэтому мы подходим к проблеме с настроенным на набор мышлением
    • не отключайте двигатель до обработки строк; это is очень медленно
    • и лишних
  • подзапросы-это обычный SQL. Синтаксис, который я использую, - это прямой ISO/IEC / ANSI SQL.
    • если вы не можете кодировать подзапросы в SQL, вы будете очень ограничено; и затем необходимо ввести дублирование данных или использовать большие результирующие наборы в качестве материализованных представлений или временных таблиц или всех видов дополнительных данных и дополнительной обработки, которые будет ы.л.о'.Вт to В. Е. Р.г с. л.о'.Вт, не говоря уже о совершенно ненужное
    • если есть что-то, что вы не можете сделать в действительно реляционной базе данных (и мои модели данных всегда) без переключения на обработку строк или встроенные представления или временные таблицы, обратитесь за помощью, что вы и сделали здесь.
  • вам нужно полностью понять первый подзапрос (более простой), прежде чем пытаться понять второй; и т. д.

метод

сначала создайте внешний запрос, используя минимальные соединения и т. д., На основе структура результирующего набора, который вам нужен, и ничего больше. Очень важно, чтобы сначала была разрешена структура внешнего запроса; в противном случае вы будете ходить взад и вперед, пытаясь сделать подзапрос подходящим для внешнего запроса, и наоборот.

  • это также требует подзапроса. Так что пока оставь эту часть., и заберешь это позже. На данный момент внешний запрос получает all (не un-acknowledged) Alerts после определенной даты

на ▶SQL-кода◀ требуется на странице 1 (извините, функции редактирования so ужасны, он уничтожает форматирование, и код уже отформатирован).

затем создайте подзапрос для заполнения каждой ячейки.

Подзапрос (1) Вывести Alert.Value

это простой производный датум выберите Value С Reading это породило Alert. Таблицы связаны, мощность равна 1:: 1, поэтому это прямое соединение на ПК.

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

на ▶SQL-кода◀ требуется на стр. 2.

я специально дал вам смесь присоединяется к внешнему запросу против получения данных через подзапрос, чтобы вы могли учиться (вы могли поочередно получать Alert.Value через соединение, но это будет еще более громоздким).

следующий подзапрос нужен производным Alert.PeakValue. Для этого нам нужно определить временную продолжительность Alert. У нас есть начало Alert продолжительность; нам нужно определить конец продолжительности, которая является далее (временно) Reading.Value что это внутри диапазон. Это также требует подзапроса, с которым нам лучше справиться в первую очередь.

  • работайте с логикой изнутри, снаружи. Старый добрый BODMAS.

Подзапрос (2) Вывести Alert.EndDtm

немного более сложный Suquery, чтобы выбрать первый Reading.ReadingDtm, это больше или равно Alert.ReadingDtm, то есть Reading.Value который меньше или равен его Sensor.UpperLimit.

обработка 5nf Temporal Данные

для обработки временных требований в базе данных 5NF (в которой EndDateTime is не хранится, как и дубликаты данных), мы работаем над StartDateTime и EndDateTime is производные: это далее StartDateTime. Это временное понятие продолжительность.

  • технически это на один миллисекунд (независимо от разрешения для используемого типа данных) меньше.
  • однако, для того чтобы будьте благоразумны, мы можем говорить и докладывать,EndDateTime как просто Next.StartDateTime, и игнорировать один миллисекундный вопрос.
  • код должен всегда использовать >= This.StartDateTime и Next.StartDateTime.
    • это устраняет множество предотвратимых ошибок
    • обратите внимание, что эти операторы сравнения, которые связывают временную длительность и должны использоваться обычным образом, как указано выше, совершенно независимы от аналогичного сравнения операторы, связанные с бизнес-логикой, например. Sensor.UpperLimit (ie. следите за ним, потому что оба часто расположены в одном WHERE предложения, и его легко перепутать или запутаться).

на ▶SQL-кода◀ обязательно, вместе с используемыми тестовыми данными, находится на странице 3.

Подзапрос (3) Вывести Alert.PeakValue

теперь это легко. Выберите MAX(Value) С Readings между Alert.ReadingDtm и Alert.EndDtm, продолжительность Alert.

на ▶SQL-кода◀ требуется на стр. 4.

Скалярный Подзапрос

в дополнение к коррелированным Подзапросам, все вышеперечисленное Скалярные Подзапросы, поскольку они возвращают одно значение; каждая ячейка в сетке может быть заполнена только одним значением. (Не скалярные подзапросы, возвращающие несколько значений, являются вполне законными, но не для выше.)

Подзапрос (4) Подтвержденные Оповещения

хорошо, теперь, когда у вас есть дескриптор вышеупомянутых коррелированных скалярных подзапросов, тех, которые заполняют ячейки в наборе, наборе, который определен внешним запросом, давайте рассмотрим подзапрос, который может использоваться для ограничения внешнего запроса. Мы не очень хотим все Alerts (выше), мы хотим Un-Acknowledged Alerts: идентификаторы, которые существуют в Alert, которые не существуют в Acknowledgement. Это не заполнение ячеек, это изменение контент внешнего набора. Конечно, это означает изменение WHERE предложения.

  • мы не меняем структура внешнего набора, поэтому нет никаких изменений в FROM и существующей WHERE положения.

добавить WHERE условие для исключения набора Acknowledged Alerts. 1:: 1 мощность, прямое Коррелированное соединение.

на ▶в SQL ◀код требуется на стр. 5.

разница в том, что это не-скалярный подзапрос, производя набор строк (один из столбцов). У нас есть целый набор Alerts (внешний набор) соответствует целому набору Acknowledgements.

  • сопоставление обрабатывается, потому что мы сказали движку, что подзапрос коррелируется, используя псевдоним (нет необходимости в громоздких соединениях для идентификации)
  • использовать 1, потому что мы проводим проверку существования. Визуализируйте его как столбец, добавленный в Alert set определяется внешним запросом.
  • никогда не используйте * потому что нам не нужен весь набор столбцов, и это будет медленнее
  • аналогично, неспособность использовать корреляцию означает WHERE NOT IN () требуется, но опять же, это создает определенный набор столбцов, а затем сравнивает два набора. Гораздо медленнее.

подзапрос (5) Actioned Alerts

как альтернативное ограничение на внешний запрос, для un-actioned Alerts, вместо (4) исключить множество Actioned Alerts. Прямое Коррелированное соединение.

на ▶SQL-кода◀ требуется на стр. 5.

этот код был протестирован на Sybase ASE 15.0.3 с помощью 1000 Alerts до 200 Acknowledgements, различных комбинаций; и Readings и Alerts определены в документе. Нулевое время выполнения миллисекунд (0.003 второе разрешение) для всех казней.

Если вам это нужно, вот это SQL-код▶в текстовом формате◀.

ответ на комментарий

(6) ▶Регистрация оповещения из Reading◀
Этот код выполняется в цикле (при условии), выбрав новый Readings, которые находятся вне диапазона, и создание Alerts, кроме случаев, когда применимое Alerts уже существует.

(7) ▶Загрузить Предупреждение От Чтения◀
Учитывая, что у вас есть полный набор тестовых данных для Reading, этот код использует измененную форму (6) для загрузки применимого Alerts.

Распространенная Проблема

это "просто", когда вы знаете как. Я повторяю, написание SQL без возможности написания подзапросов очень ограничено; это важно для обработки реляционных баз данных, для чего был разработан SQL.

  • половина причина, по которой разработчики реализуют ненормализованные кучи данных (массовое дублирование данных), заключается в том, что они не могут написать подзапросы, необходимые для нормализованных структур
    • дело не в том, что они "денормализованы для производительности"; дело в том, что они не могут кодировать нормализованные. Я видел это сотни раз.
    • пример здесь: у вас есть полностью нормализованная реляционная база данных, и трудность заключается в ее кодировании, и вы рассматривали возможность дублирования таблиц для обработки цели.
  • и это не считая дополнительной сложности временной базы данных; или временной базы данных 5NF.
  • нормализация означает Никогда Ничего Не Дубликат, еще недавно известный как не повторяйся
  • Master Suqueries и вы будете в 98-м процентиле: нормализованные, истинные реляционные базы данных; нулевое дублирование данных; очень высокая производительность.

я думаю, вы можете выясните оставшиеся у вас запросы.

Реляционных Идентификатор

обратите внимание, что этот пример также демонстрирует силу использования Реляционных Идентификаторы, в том, что несколько таблиц между теми, которые мы хотим, не должны быть объединены (да! правда в том, что реляционные идентификаторы означают меньше, а не больше, чем Id ключи). Просто следуйте сплошным линиям.

  • ваше временное требование требует ключей, содержащих DateTime. Представьте, что вы пытаетесь закодировать вышеизложенное с помощью Id PKs, будет два уровня обработки: один для соединений (и их будет намного больше), а другой для обработки данных.

метка

я стараюсь держаться подальше от разговорных ярлыков ("вложенных"," внутренних " и т. д.), Потому что они не являются конкретными и придерживаются конкретных технических терминов. Для полноты и понимания:

  • подзапрос после FROM п., - Это Материализовал Вид, результирующий набор, полученный в одном запросе, а затем введенный в FROM предложение другого запроса, как "таблица".
    • типы Oracle называют это встроенное представление.
    • в большинстве случаев вы можете писать коррелированные подзапросы как материализованные представления, но это значительно больше ввода-вывода и обработки (поскольку обработка подзапросов оракулами является бездонной, только для Oracle материализованные представления "быстрее").
      .
  • подзапрос в WHERE статья Подзапрос Предикат, потому что он изменяет содержание результирующего набора (того, на чем он основан). Он может возвращать либо скалярное (одно значение), либо не скалярное (много значений).

    • для скаляров, используйте WHERE column =, или любой скалярный оператор

    • для не-скаляры, используйте WHERE [NOT] EXISTS, или WHERE column [NOT] IN

  • Suquery в WHERE пункт не нужно для корреляции; следующее работает просто отлично. Определите все лишние придатки:


попробуйте это:

SELECT MIN(TableDTM) TableDTM, Code
FROM
(
    SELECT T1.TableDTM, T1.Code, MIN(T2.TableDTM) XTableDTM
    FROM T T1
    LEFT JOIN T T2
    ON T1.TableDTM <= T2.TableDTM
    AND T1.Code <> T2.Code
    GROUP BY T1.TableDTM, T1.Code
) X
GROUP BY XTableDTM, Code
ORDER BY 1;

PostgreSQL поддерживает оконные функции, посмотрите на этой

[редактирование] Попробуйте следующее:

SELECT TableDTM, Code FROM
(
    SELECT TableDTM,
           Code,
           LAG(Code, 1, NULL) OVER (ORDER BY TableDTM) AS PrevCode
    FROM   T
)
WHERE PrevCode<>Code OR PrevCode IS NULL;

не могли бы вы попробовать что-то вроде

"SELECT DISTINCT Code, (SELECT MIN(TableDTM) FROM T AS Q WHERE Q.Code = T.Code) As TableDTM FROM T;"

и если вам нужно исключить 0, то измените его на:

 SELECT DISTINCT Code, (SELECT MIN(TableDTM) FROM T AS Q WHERE Q.Code = T.Code) As TableDTM FROM T WHERE Code <> 0;