2012-03-01 2 views
1

Существует ли не грубая сила/эффективный способ определения минимального значения, поддерживаемого в течение x минут из таблицы sql со структурой, как показано ниже? Эта таблица будет иметь 1 запись на case_id за канал_индекса в секунду, и в этой таблице может быть 20 каналов на каждый случай и тысячи случаев. Мне нужно будет выполнить этот запрос для каждого случая и для каждого канала. Мне нужно найти наименьшее и самое высокое значение, которое произошло в течение 3 последовательных минут.Как найти наименьшее устойчивое значение

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

case_id  channel_index start_time    dms_value    value_duration 
----------- ------------- ----------------------- ---------------------- -------------- 
2668  0    2011-09-28 10:24:39.000 69.5769729614258  2 
2668  0    2011-09-28 10:24:41.000 69.7469329833984  2 
2668  0    2011-09-28 10:24:43.000 69.8547210693359  1 
2668  0    2011-09-28 10:24:44.000 69.8475494384766  1 
2668  0    2011-09-28 10:24:45.000 69.9703216552734  2 
2668  0    2011-09-28 10:24:47.000 69.9699172973633  1 
2668  0    2011-09-28 10:24:48.000 70.0099258422852  2 
2668  0    2011-09-28 10:24:50.000 70.2449035644531  1 
2668  0    2011-09-28 10:24:51.000 70.0424575805664  2 
2668  0    2011-09-28 10:24:53.000 70.1216125488281  1 
2668  0    2011-09-28 10:24:54.000 69.5616912841797  1 
2668  0    2011-09-28 10:24:55.000 69.5902786254883  2 
2668  0    2011-09-28 10:24:57.000 70.0330581665039  1 
2668  0    2011-09-28 10:24:58.000 70.4709854125977  1 
2668  0    2011-09-28 10:24:59.000 70.7001647949219  2 
2668  0    2011-09-28 10:25:01.000 70.274040222168  1 
2668  0    2011-09-28 10:25:02.000 69.7524795532227  1 
2668  0    2011-09-28 10:25:03.000 69.4606552124023  2 
2668  0    2011-09-28 10:25:05.000 69.6096954345703  1 
2668  0    2011-09-28 10:25:06.000 69.8238906860352  1 

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

Например, из приведенного выше набора данных, если бы я хотел знать наименьшее значение в течение 5 последовательных секунд, это будет 69.8238906860352. Если бы мне это понадобилось в течение 8 последовательных секунд, это было бы 69.9703216552734.

Вот полная структура таблицы:

CREATE TABLE [dbo].[continuous_data](
    [case_id] [int] NOT NULL, 
    [channel_index] [smallint] NOT NULL, 
    [start_time] [datetime] NOT NULL, 
    [dms_type] [char](1) NOT NULL, 
    [dms_value] [float] NOT NULL, 
    [value_duration] [smallint] NOT NULL, 
    [error_code] [int] NULL 
) ON [PRIMARY] 

EDIT 3-5-12: SO я реализовал грубую силу так, чтобы вычислить минимальные устойчивые значения и, кажется, работает хорошо, когда конкретный случай имеет несколько тысяч записей, но при тестировании на случай, который имел 1,1 миллиона, я закончил отмену его через 37 минут ... Вот код, который я использую. У кого-нибудь есть идеи по оптимизации?

ALTER procedure [dbo].[GetSustainedValues]( 
    @case_id int, 
    @time_limit int, 
    @bypass_only bit = NULL) 
as 
begin 

DECLARE @time DateTime, @channelindex int, @lastchannelindex int 
DECLARE @tmin float, @tmax float, @min float, @max float, @caseid int 

DECLARE @results TABLE(case_id int, channel_index int, max float null, min float null) 
DECLARE CursorName CURSOR FAST_FORWARD 
    FOR SELECT start_time, channel_index from continuous_data where case_id = @case_id order by channel_index, start_time 
OPEN CursorName 
FETCH NEXT FROM CursorName INTO @time, @channelindex 
SET @lastchannelindex = @channelindex 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    --PRINT 'hello' --'Chennel:' + CONVERT (VARCHAR(50), @channelindex,128) + ' Time:' + CONVERT (VARCHAR(50), @time,128) 
    IF @lastchannelindex != @channelindex 
    BEGIN 
     --PRINT 'Starting new channel:' + CONVERT (VARCHAR(50), @channelindex,128) 
     -- we are starting on a new channel so insert that data into the results 
     -- table and reset the min/max 
     INSERT INTO @results(case_id, channel_index, max, min) VALUES(@case_id, @lastchannelindex, @max, @min) 
     SET @max = null 
     SET @min = null 
     SET @lastchannelindex = @channelindex 
    END 

    Select @tmax = MAX(dms_value), @tmin = MIN(dms_value) 
    from continuous_data 
    where case_id = @case_id and channel_index = @channelindex and start_time between DATEADD(s, -(@time_limit-1), @time) and @time 
    HAVING SUM(value_duration) >= @time_limit 
    IF @@ROWCOUNT > 0 
    BEGIN 
     IF @max IS null OR @tmin > @max 
     BEGIN 
      --PRINT 'Setting max:' + CONVERT (VARCHAR(50), @tmin,128) + ' for channel:' + CONVERT (VARCHAR(50), @channelindex,128) 
      set @max = @tmin 
     END 

     IF @min IS null OR @tmax < @min 
     BEGIN 
      set @min = @tmax 
     END 
    END 
    --PRINT 'Max:' + CONVERT (VARCHAR(50), @max,128) + ' Min:' + CONVERT (VARCHAR(50), @min,128) 
    FETCH NEXT FROM CursorName INTO @time, @channelindex 
END 
CLOSE CursorName 
DEALLOCATE CursorName 
--PRINT 'Max:' + CONVERT (VARCHAR(50), @max,128) + ' Min:' + CONVERT (VARCHAR(50), @min,128) 
SELECT * FROM @results 
end 

EDIT: 3-7-2012 До сих пор не нашли ответа. Есть ли более эффективный способ сделать это, используя DLL .Net, которая может быть вызвана из хранимой процедуры? Ищите любые предложения здесь. Благодаря!

+0

Является ли продолжительность продолжительности в минутах, и всегда ли она 1 или 2? – Phil

+0

№ Значение value может быть любым. Набор выборочных данных, который я выбрал, имеет только 1 или 2. Это просто разница во времени между выборками в секундах. – mdutra

+0

Когда вы говорите наименьшее устойчивое значение, вы имеете в виду, что dms_value должно быть равно или ниже в течение всего времени? – Martin

ответ

0

Я не уверен, полностью ли я понимаю ваш вопрос, но вы имеете в виду что-то вроде этого?

select min(value_duration) as lowest, max(value_duration) as highest from mytable where case_id=2668 and start_time between Cast('2011-09-28 10:26:04' as datetime) and cast('2011-09-28 10:26:07' as datetime); 

Этот запрос получает самый низкий и самый высокий value_duration для случая 2668, между 10:26:04 и 10:26:07.

Надеюсь, это поможет!

+0

Этот запрос возвращает только одно наивысшее/наименьшее значение за период времени. Мне нужно то, что является наивысшим/наименьшим постоянным значением в течение 3 минут. – mdutra

+0

Как часто вам нужно проверять минимальный/самый высокий за эти 3 минуты? Раз в секунду? 10 секунд? Минута? –

+0

Планируется, что при заполнении заполненного флага наименьшее/самое высокое значение будет рассчитано для каждого канала и сохранено в случае. Так что это произойдет только один раз. – mdutra

0

выберите top 3 value_duration from mytable, где case_id = 2668 и start_time между Cast ('2011-09-28 10:26:04' в качестве даты и времени) и cast ('2011-09-28 10:26:07' as datetime) order by value_duration union select top 3 value_duration from mytable где case_id = 2668 и start_time между Cast ('2011-09-28 10:26:04' как дата и время) и cast ('2011-09-28 10:26 : 07 'в качестве даты и времени) order by value_duration union DESC;

0

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

Концептуально, он должен работать, как это (на Oracle):

select case_id, channel_index, 
    min(su_min) as sustained_min, 
    max(su_max) as sustained_max 
from (
    select case_id, channel_index, start_time, 
     min(dms_value) over (partition by case_id, channel_index order by start_time 
      range numtodsinterval(3, 'minute') preceeding) as su_max, 
     max(dms_value) over (partition by case_id, channel_index order by start_time 
      range numtodsinterval(3, 'minute') preceeding) as su_min, 
     min(start_time) over (partition by case_id, channel_index order by start_time) 
      as first_time 
    from data_table order by start_time 
    ) as su_data 
where 
    first_time + numtodsinterval(3, 'minute') <= start_time 
group by 
    case_id, channel_index 

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

По существу, вы сначала вычисляете значения min/max за все интервалы времени, затем вы выбираете самый высокий/самый низкий из них. Предложение first_time используется для исключения значений в начале временного ряда.

EDIT: Решение на самом деле для Oracle, но важные биты являются стандартными SQL: 2003.

+0

Я проверю это. Кроме того, это на SQL Server 2008, а не на Oracle, поэтому я увижу, есть ли эквивалентный оператор. – mdutra

+0

Да, извините, у меня возникли два вопроса. Оператор должен быть чистым SQL: 2003 (за исключением бита numtointerval), поэтому есть шанс, что он поддерживает sqlserver. – Martin

+0

На самом деле у меня проблема с функцией диапазона. Кажется, что нет эквивалента в SQL Server. Что именно это делает? – mdutra

0

Это проверено на основе ваших данных и соответствует вашим результатам.

Я делаю самостоятельное соединение на столе [Case] ​​и фильтрую для строк, где c1.start_time < c2.start_time.

Затем выбирая строки, где end_time-start_time = желаемое разное время.

Затем с использованием подзапроса для получения max (dms_value) в этом диапазоне.

Затем группировка case_id, channel_index и поиск min (max (dms_value)).

declare @timeDiff int = 5 -- seconds 

select case_id, channel_index, MIN(dms_max) 
from 
(
    select t2.*, 
     (select MAX(dms_value) 
      from [Case] c 
      where 
       c.case_id = t2.case_id and 
       c.channel_index = t2.channel_index and 
       c.start_time between t2.start_time and t2.end_time) dms_max 
    from 
    (
     select * from (
      select c1.case_id, c1.channel_index, c1.start_time, c2.start_time end_time from [Case] c1 
      inner join [Case] c2 on 
       c1.case_id = c2.case_id and 
       c1.channel_index = c2.channel_index and 
       c1.start_time < c2.start_time 
     ) 
     t 
     where DATEDIFF(ss, start_time, end_time) 
      between @timeDiff - 1 and @timeDiff - 1 + 
      (select max(value_duration) from [Case] c where c.case_id = case_id and c.channel_index = channel_index) 
    ) 
    t2 
) 
t3 
group by case_id, channel_index 
Смежные вопросы