Найти период превышения скорости? - PullRequest
3 голосов
/ 01 июня 2010

Просто что-то интересное пришло мне в голову. Предположим, что у нас есть таблица (в SQL Server), подобная этой:

  • Расположение
  • Скорость
  • Время

например:

Location     Velocity   Time
1            40         1:20
2            35         2:00
3            45         2:05
4            50         2:30
5            60         2:45
6            48         2:55
7            40         3:00
8            35         3:15
9            50         3:20
10           70         3:30
11           50         3:35
12           40         3:40

Предположим, что скоростной барьер составляет 40 км / ч, на выходе что-то вроде этого

Starttime         Endtime
2:05              3:00
3:20              3:35 

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

(псевдо-код C #)

bool isOverSpeed = false;

for (int i =0;i<arr.Length;i++)
{
if (!isOverSpeed)
    if (arr[i].Velocity > speedBarrier)
        {
            #insert the first record into another array.
            isOverSpeed = true;
        }
if(isOverSpeed)

    if (arr[i].Velocity < speedBarrier)
          {
          #insert the record into that array
          isOverSpeed = false;
          }

}

Это работает, но несколько "не очень эффективно". Есть ли более «умный» способ, например, запрос T-SQL или другой алгоритм для этого?

Ответы [ 4 ]

2 голосов
/ 01 июня 2010

Этого можно добиться с помощью CTE ( Общие табличные выражения ).

Приведенный ниже запрос работает с демонстрационной таблицей Adventure Works на SQL Server («ограничение скорости» равно 7).

Это сильно вдохновлено другим вопросом о SO: GROUP BY для непрерывных строк в SQL .

with CTE as (
    select
        ROW_NUMBER() over(order by SalesTaxRateID) as RowNo
        , *
    from
        Sales.SalesTaxRate
)
, MyLogGroup as (
    select
        l.*
        ,(select
              max(SalesTaxRateID)
          from
              CTE c
          where
              not exists (select * from CTE
                              where RowNo = c.RowNo-1
                              and TaxRate > 7
                              and c.TaxRate > 7)
              and c.SalesTaxRateID <= l.SalesTaxRateID) as GroupID
    from
        Sales.SalesTaxRate l)
select
    min(SalesTaxRateID) as minimum
    , max(SalesTaxRateID) as maximum
    , avg(TaxRate)
from
    MyLogGroup
group by
    GroupID
having
    min(TaxRate) > 7
order by
    minimum

Что-то в этом духе должно вас устроить:

with CTE as (
    select
        ROW_NUMBER() over(order by [Time]) as RowNo
        , *
    from
        <table_name>
)
, MySpeedGroup as (
    select
        s.*
        ,(select
              max([Time])
          from
              CTE c
          where
              not exists (select * from CTE
                              where RowNo = c.RowNo-1
                              and Velocity > <speed_limit>
                              and c.Velocity > <speed_limit>)
              and c.[Time] <= s.[Time]) as GroupID
    from
        <table_name> l)
select
    min([Time]) as minimum
    , max([Time]) as maximum
    , avg([Velocity]) -- don't know if you want this
from
    MySpeedGroup
group by
    GroupID
having
    min(Velocity) > <speed_limit>
order by
    minimum
1 голос
/ 01 июня 2010

Я использовал следующую часть для получения некоторых данных (я нахожусь в режиме совместимости 80 атм, поэтому у меня нет поля времени и я использую INT для метки времени)

DECLARE @Info TABLE (Location INT IDENTITY, Velocity INT, [Time] INT);
INSERT INTO @Info (Velocity, [Time]) VALUES (40, 80);
INSERT INTO @Info (Velocity, [Time]) VALUES (35, 120);
INSERT INTO @Info (Velocity, [Time]) VALUES (45, 125);
INSERT INTO @Info (Velocity, [Time]) VALUES (50, 150);
INSERT INTO @Info (Velocity, [Time]) VALUES (60, 165);
INSERT INTO @Info (Velocity, [Time]) VALUES (48, 175);
INSERT INTO @Info (Velocity, [Time]) VALUES (40, 180);
INSERT INTO @Info (Velocity, [Time]) VALUES (35, 195);
INSERT INTO @Info (Velocity, [Time]) VALUES (50, 200);
INSERT INTO @Info (Velocity, [Time]) VALUES (70, 210);
INSERT INTO @Info (Velocity, [Time]) VALUES (50, 215);
INSERT INTO @Info (Velocity, [Time]) VALUES (40, 220);
INSERT INTO @Info (Velocity, [Time]) VALUES (45, 225);
INSERT INTO @Info (Velocity, [Time]) VALUES (45, 230);

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

DECLARE @Limit INT;
SET @Limit = 40;

WITH Stage1 ([Location], [Velocity], [Time]) AS (
    SELECT * FROM @Info WHERE [Velocity] > @Limit
), Stage2 (Start) AS (
    SELECT [Time]
      FROM [Stage1]
     WHERE ([Location] - 1) NOT IN (SELECT [Location] FROM [Stage1])
), Stage3 ([Start], [Stop]) AS (
    SELECT [Start]
         , (SELECT MIN([Time]) FROM [Stage1] WHERE ([Location] + 1) NOT IN (SELECT [Location] FROM [Stage1]) AND [Time] > [Stage2].[Start])
      FROM Stage2
)
SELECT *
  FROM Stage3
1 голос
/ 01 июня 2010

Это не может быть , что просто, или не так ли?

SELECT
  Location,
  Velocity,
  Time,
  CASE WHEN Velocity > @SpeedBarrier THEN 1 ELSE 0 END AS IsOverSpeed
FROM
  SpeedTable
0 голосов
/ 01 июня 2010

Предполагая, что если он совпадает, он будет ложным, следуйте SQL-запросу ниже

выберите местоположение, скорость, время, (скорость> speedLimit = истина ИЛИ скорость <= speedLimit = ложь) как превышение скорости от Таблица где speedLimit = yourGivenLimit </p>

Я не проверял это, но я уверен, что что-то подобное подойдет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...