Фильтрация повторяющихся значений во время выполнения в базе данных sql - на основе набора - PullRequest
0 голосов
/ 23 сентября 2008

У меня есть проблема с базой данных, которую я в настоящее время не могу обернуть головой с помощью простого решения. В моей БД у меня есть таблица, в которой хранятся значения событий .. 0 и 1 с отметкой времени. Проблема в том, что одно и то же событие может происходить дважды, как бизнес-правило. Как ниже

  • '2008-09-22 16: 28: 14,133', 0
  • '2008-09-22 16: 28: 35,233', 1
  • '2008-09-22 16: 29: 16,353', 1
  • '2008-09-22 16: 31: 37,273', 0
  • '2008-09-22 16: 35: 43,134', 0
  • '2008-09-22 16: 36: 39,633', 1
  • '2008-09-22 16: 41: 40,733', 0

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

Есть идеи или предложения?

Спасибо

Ответы [ 4 ]

1 голос
/ 23 сентября 2008

(предисловие ....... я сделал это только в oracle, но я уверен, что если БД поддерживает триггеры, это все возможно)

Иметь триггер перед вставкой, который выбирает строку с максимальным значением метки времени. Если значение этой строки совпадает со значением, которое вы хотите вставить, игнорируйте его.

Это должно держать их всех в правильном состоянии.

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

0 голосов
/ 23 сентября 2008

При этом используется общее табличное выражение SQL Server, но оно может быть встроенным, с таблицей t со столбцами dt и cyctatetate:

;WITH Firsts AS (
    SELECT t1.dt
        ,MIN(t2.dt) AS Prevdt
    FROM t AS t1
    INNER JOIN t AS t2
        ON t1.dt < t2.dt
        AND t2.cyclestate <> t1.cyclestate
    GROUP BY t1.dt
)
SELECT MIN(t1.dt) AS dt_start
    ,t2.dt AS dt_end
FROM t AS t1
INNER JOIN Firsts
    ON t1.dt = Firsts.dt
INNER JOIN t AS t2
    ON t2.dt = Firsts.Prevdt
    AND t1.cyclestate <> t2.cyclestate
GROUP BY t2.dt
    ,t2.cyclestate
HAVING MIN(t1.cyclestate) = 0
0 голосов
/ 23 сентября 2008

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

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

Конечно, вы можете использовать все возможности среды базы данных, чтобы определить «изменение входного набора данных» и сохранить отфильтрованные данные. Так что это может быть легко или сложно, как вы хотите.

0 голосов
/ 23 сентября 2008

Просто чтобы я понял проблему.

Если вы упорядочиваете набор строк на основе метки времени, иногда повторяются значения, встречающиеся рядом друг с другом, как, например, приведенная выше пара 1 в элементе 2nd и 3rt? а потом у вас двойные 0 в 4-м и 5-м, это так?

А вы хотите последнюю из соответствующей пары (или последовательности, если их больше 2)?

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

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

Вот SQL, чтобы получить все:

SELECT timestamp, value
FROM yourtable

А вот как присоединиться, чтобы получить минимальную метку времени выше текущей:

SELECT T1.timestamp, MIN(T2.timestamp) AS next_timestamp, T1.value
FROM yourtable T1, yourtable T2
WHERE T2.timestamp > T1.timestamp
GROUP BY T1.timestamp, T1.value

(Боюсь, что приведенный выше запрос будет ужасно медленным)

И затем извлечь значение, соответствующее этой минимальной временной отметке

SELECT T3.timestamp, T3.value
FROM (
    SELECT T1.timestamp, MIN(T2.timestamp) AS next_timestamp, T1.value
    FROM yourtable T1, yourtable T2
    WHERE T2.timestamp > T1.timestamp
    GROUP BY T1.timestamp, T1.value
) T3, yourtable AS T4
WHERE T3.next_timestamp = T4.timestamp
  AND T3.value <> T4.value

К сожалению, это не дает последнего значения, так как для сравнения необходимо следующее значение. Простое фиктивное значение Sentinel (вы можете объединить это, если вам нужно) будет обрабатывать это.

Вот дамп базы данных sqlite, с которым я проверял вышеуказанный запрос:

BEGIN TRANSACTION;
CREATE TABLE yourtable (timestamp datetime, value int);
INSERT INTO "yourtable" VALUES('2008-09-22 16:28:14.133',0);
INSERT INTO "yourtable" VALUES('2008-09-22 16:28:35.233',1);
INSERT INTO "yourtable" VALUES('2008-09-22 16:29:16.353',1);
INSERT INTO "yourtable" VALUES('2008-09-22 16:31:37.273',0);
INSERT INTO "yourtable" VALUES('2008-09-22 16:35:43.134',0);
INSERT INTO "yourtable" VALUES('2008-09-22 16:36:39.633',1);
INSERT INTO "yourtable" VALUES('2008-09-22 16:41:40.733',0);
INSERT INTO "yourtable" VALUES('2099-12-31 23:59:59.999',2);
COMMIT;

А вот (форматированный) вывод:

timestamp                 value
2008-09-22 16:28:14.133   0
2008-09-22 16:29:16.353   1
2008-09-22 16:35:43.134   0
2008-09-22 16:36:39.633   1
2008-09-22 16:41:40.733   0
...