2016-08-04 3 views
0

Я использую SQL Server 12.0.4213.0 и новичок в продвинутых SQL-запросах.SQL Parse Duration String to Seconds

У меня есть столбец с именем nvarcharдлительность, содержащий timespans в одном из двух форматов: 39 Seconds если продолжительность составляет менее 1 минуты, иначе формат подобен 1 Day 18 Hours 2 Minutes. Обратите внимание, что каждый день/час/минута/секунда может быть сингулярным или множественным. Также в первом формате, если день равен 0, День будет опущен (то же самое с Часом).

Мне нужно преобразовать продолжительность в целое число, представляющее итоговые секунды. 1 Minute 30 Seconds должен вернуть 90 и 39 Seconds должен вернуть 39.

Я думаю, что это может быть выполнено с помощью вложенного оператора CASE. Набор данных очень велик, поэтому я хочу быть уверенным, что я очень эффективно подхожу к этому. Любая помощь будет оценена по достоинству.

Один метода я работал в PowerShell заключается в разделении длительности столбца на символе пробела, цикл через результирующий массив увеличивающегося счетчика на 2, принимает array[counter] и умножить его на соответствующем значении секунд определяются array[counter+1], добавьте продукт на общую сумму секунд.

+0

Должно быть возможно добиться того, что вы хотите, используя строковые функции. Но зачем страдать от такой боли, когда вы можете ее избежать? Храните ваши метки времени как ... timestamps вместо 'nvarchar'. –

+0

@TimBiegeleisen - Я согласен с вами, однако я не являюсь администратором этой БД и строго являюсь потребителем данных. – Zachafer

+0

Что делать, если это всего лишь 2 минуты. Он говорит 0 дней 0 часов 2 минуты? – scsimon

ответ

1

Это немного сложно, но не страшно. Способ, которым вы можете решить это, заключается в удалении ярлыков Day (s), Hours (s), Minute (s), Second (s) из строки и их заменянии некоторой разделительной строкой/символом.

Затем вы можете разделить полученные значения с помощью CTE.

Затем вы создадите промежуток времени в секундах от каждой части на основе ее местоположения в разделительной строке.

Наконец, суммируйте полученные результирующие промежутки времени-часть-секунды.

EDIT: Теперь определяется как функция

Как так:

CREATE FUNCTION fn_getDurationSeconds(@duration varchar(100)) returns int 
as 
BEGIN 
    DECLARE @durationSeconds INT = 0; 

    with split (duration, part, remainder) as 
    (
     SELECT 
      duration, 
      left(duration, CHARINDEX(':', duration)-1), 
      right(duration, LEN(duration) - CHARINDEX(':', duration)) 
     FROM 
     (
      SELECT 
       REPLACE(
        REPLACE(
         REPLACE(
          REPLACE(
           REPLACE(
            REPLACE(
             CASE WHEN @duration LIKE '% Day' or @duration like '% Days' THEN @Duration + ' 0 Hours 0 Minutes 0 Seconds' 
              WHEN @duration LIKE '% Hour' or @duration like '% Hours' THEN @Duration + ' 0 Minutes 0 Seconds' 
              WHEN @duration LIKE '% Minute'or @duration like '% Minutes' THEN @Duration + ' 0 Seconds' 
              ELSE @Duration 
             END, ' Day', ':'), 
            ' Hour', ':'), 
           ' Minute', ':'), 
          ' Second', ':00'), 
         's', ''), 
        ' ', '') duration 

     ) t 
     union all 
     select 
      duration, 
      left(remainder, CHARINDEX(':', remainder) - 1), 
      right(remainder, LEN(remainder) - CHARINDEX(':', remainder)) 
     from split 
     where CHARINDEX(':', remainder) > 0 
    ) 
    SELECT @durationSeconds = sum(seconds) 
    FROM 
    (
     select 
      duration, 
      CASE row_number() over(partition by duration order by LEN(remainder)) 
       WHEN 4 THEN 24 * 60 * 60 
       WHEN 3 THEN 60 * 60 
       WHEN 2 THEN 60 
       WHEN 1 THEN 1 
      END * CAST(part AS INT) Seconds 
     from split 
    ) t 
    group by duration 
    order by duration 


    RETURN @durationSeconds 
END 
+0

Благодарю вас за ответ. Как создать функцию (например, calcSecs), где я могу передать продолжительность и вернуть int seconds? В моем запросе мне нужно будет «SELECT ID», DurationSec = SUM (calcSecs ([duration])) .... GROUP BY ID' – Zachafer

+0

Реализована ошибка в этом коде (минуты считаются секундами, когда секунды не указаны) .-- исправлено! Я бы не рекомендовал использовать функцию здесь. Функции - это процесс с одним потоком, который замедлит ваши запросы. Моей рекомендацией было бы использовать эту логику в хранимой процедуре, если это возможно. – mrtig

+0

На всякий случай вам нужна функция в любом случае, я обновил свой ответ, чтобы быть в форме функции. – mrtig

0

Другой вариант заключается в использовании substring и patindex и пару общих табличных выражений, чтобы получить желаемый результат:

Сначала создайте и заполните таблицу образцов: (Пожалуйста, спасите нас этот шаг в ваших будущих вопросах)

DECLARE @T AS TABLE 
(
    Duration varchar(40), 
    NumberOfSeconds int 
) 

INSERT INTO @T (Duration) VALUES 
('39 Seconds'), 
('2 Minutes 4 Seconds'), 
('1 Hours 7 Minutes 17 Seconds'), 
('3 Days 9 Hour 8 Minutes 25 Seconds') 

Затем используйте один КТР провести patindex результатов для каждого слова.
Этот cte только для того, чтобы избавить нас от необходимости писать patindex снова и снова. Добавить еще КТР к этому, что будет использовать substring для извлечения значений для каждой временной части, используя первый КТР:

;WITH cte1 as 
(
    SELECT *, 
      PATINDEX('%Day%', Duration) As DaysIndex, 
      PATINDEX('%Hour%', Duration) As HoursIndex, 
      PATINDEX('%Minute%', Duration) As MinutesIndex, 
      PATINDEX('%Second%', Duration) As SecondIndex 
    FROM @T 
), cte2 as 
(
SELECT *, 

     CASE WHEN DaysIndex > 0 THEN 
      CAST(SUBSTRING(Duration, 0, DaysIndex) As int) * 60 * 60 * 24 
     ELSE 
      0 
     END As D, 
     CASE WHEN HoursIndex > 0 THEN 
      CAST(
      SUBSTRING(Duration, 
       CASE WHEN DaysIndex > 0 THEN DaysIndex + 4 
       ELSE 0 
       END, 

       CASE WHEN DaysIndex > 0 THEN HoursIndex - DaysIndex - 4 
       ELSE HoursIndex 
       END 
      ) As int) * 60 * 60 
     ELSE 
      0 
     END As H, 
     CASE WHEN MinutesIndex > 0 THEN 
      CAST(
      SUBSTRING(Duration, 
       CASE WHEN HoursIndex > 0 THEN HoursIndex + 5 
       ELSE 0 
       END, 

       CASE WHEN HoursIndex > 0 THEN MinutesIndex - HoursIndex - 5 
       ELSE MinutesIndex 
       END 
      ) As int) * 60 
     ELSE 
      0 
     END As M, 
     CASE WHEN SecondIndex > 0 THEN 
      CAST(
      SUBSTRING(Duration, 
       CASE WHEN MinutesIndex > 0 THEN MinutesIndex + 7 
       ELSE 0 
       END, 

       CASE WHEN MinutesIndex > 0 THEN SecondIndex - MinutesIndex - 7 
       ELSE SecondIndex 
       END 
      ) As int) 
     ELSE 
      0 
     END As S 
FROM cte1 
) 

Теперь обновите NumberOfSeconds столбец:

UPDATE cte2 
SET NumberOfSeconds = D + H + M + S 

Verify обновление результаты:

SELECT * 
FROM @T 

результаты:

Duration         NumberOfSeconds 
---------------------------------------- --------------- 
39 Seconds        39 
2 Minutes 4 Seconds      124 
1 Hours 7 Minutes 17 Seconds    4037 
3 Days 9 Hour 8 Minutes 25 Seconds  292105