2015-10-20 3 views
0

Редакцией:Проверить наличие различных значений для каждой группы

Предположим, у меня есть следующая таблица в MySQL:

CREATE TABLE `events` (
`pv_name` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL, 
`time_stamp` bigint(20) UNSIGNED NOT NULL, 
`value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, 
PRIMARY KEY (`pv_name`, `time_stamp`) 
) ENGINE=InnoDB; 

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

SELECT events.pv_name 
FROM events 
GROUP BY events.pv_name 
HAVING COUNT(DISTINCT events.value) > 1; 

Проблема в том, что этот запрос неэффективен. Он подсчитывает все отдельные значения вместо того, чтобы останавливаться после нахождения более одного.

Одно предложение было следующее:

SELECT events.pv_name 
FROM events 
GROUP BY events.pv_name 
HAVING MIN(events.value) < MAX(events.value); 

Это эффективно, если индекс включает в себя value. Тем не менее, value является текстовым столбцом, поэтому он не может.

Есть ли другой подход, который сделает этот поиск более эффективным? Возможно, существует некоторая форма коррелированного подзапроса? Я хотел бы остаться с MySQL, но если в другом сервере базы данных есть функция, которая поможет мне, я могу подумать о переходе на нее.

+0

, как часто возникают вставки? Каковы требования к точности своевременности? – Drew

+0

У меня нет фиксированного номера для скорости вставки. Это может быть так же быстро, как позволяет база данных. Я не уверен, что вы подразумеваете под точностью своевременности? – Patrick

+0

Может ли быть отставание в том, что статистические данные точны. Этот тип информации исключает или делает возможными различные стратегии. – Drew

ответ

0

Чтобы ответить на ваш вопрос, вероятно, лучше избегать group by или distinct. Во-первых, я бы предложил добавить для таблицы автоматически сгенерированный event_id. Это позволяет определить, совпадают ли две строки.

Итак, я хотел бы предложить следующий запрос:

select e.* 
from events e 
where e.time_stamp between $ts1 and $ts2 and 
     exists (select 1 
       from events e2 
       where e2.pv_name = e.pv_name and 
        e2.time_stamp between $ts1 and $ts2 and 
        e2.event_id < e.event_id 
      ); 

Вы также хотите индексы: events(time_stamp, pv_name, event_id) и events(pv_name, time_stamp, event_id).

Это находит пары событий. Вы можете использовать select distinct pv_name. Тем не менее, это вызывает кучу дополнительной обработки для удаления дубликатов.

+0

Это, похоже, не сравнивает значения? – Patrick

+0

Сравните, какие значения? Вы спрашиваете о получении списков имен. –

+0

Имена, которые имеют более одного отдельного значения за данный интервал времени. – Patrick

0
SELECT * FROM Customers WHERE pv_name IN 
(SELECT pv_name FROM Customers GROUP BY pv_name HAVING COUNT(*) > 1) AND 
time_stamp BETWEEN 'start_time' and `end_time' 

ИЛИ

SELECT * FROM Customers GROUP BY pv_name HAVING MIN(time_stamp) < MAX(time_stamp);

Это может сработать.

0

Я считаю, что следующие могут работать? Можно ли это улучшить?

-- Chooses a single non null `value` from the `events` table for each `pv_name`. 
CREATE TEMPORARY TABLE single_values (PRIMARY KEY (pv_name)) ENGINE=Memory AS (
SELECT events.pv_name, events.value 
FROM events 
WHERE events.value IS NOT NULL 
GROUP BY events.pv_name); 

-- Finds each `pv_name` that has a `value` different than the one for it in `single_values`. 
-- This is a correlated subquery. 
SELECT single_values.pv_name 
FROM single_values 
WHERE 1 = (
SELECT 1 
FROM events 
WHERE events.pv_name = single_values.pv_name 
AND events.value <> single_values.value 
AND events.value IS NOT NULL 
LIMIT 1); 
Смежные вопросы