2016-08-04 11 views
0

私はSQL Server 12.0.4213.0を使用しており、高度なSQLクエリは初めてです。 SQL解析時間の文字列を秒に変換

は、私は2つの形式のいずれかでのタイムスパンを含む 期間という名前 nvarchar列を持っている: 39 Secondsを持続時間は、1分未満であれば、他のフォーマットが 1 Day 18 Hours 2 Minutesのようなものです。各日/時/分/秒は、単数形または複数形にすることができます。また、最初の形式では、曜日が0の場合は曜日が省略されます(Hourと同じ)。

期間を合計秒を表す整数に変換する必要があります。 1 Minute 30 Secondsは90を返し、39 Secondsは39を返します。

これはネストされたCASE文で完了できると思います。データセットは非常に大きいので、私はこれに効率的にアプローチしていることを確認したい。どんな助けもありがとう。

私はPowerShellで働いてきた一つの方法は、空白文字、2によって生じた配列インクリメントカウンタをループ上の期間列を分割することで、array[counter]を取り、array[counter+1]によって決定された対応秒の値を掛け、追加積を合計秒数に加算します。

+0

文字列関数を使用して必要なものを実現することは可能です。しかし、なぜあなたはそれを避けることができるときにそのような痛みに苦しむ? 'nvarchar'ではなくtimestampsとしてタイムスタンプを保存します。 –

+0

@TimBiegeleisen - 私はあなたに同意しますが、私はこのDBの管理者ではなく、厳密にデータの消費者です。 – Zachafer

+0

わずか2分であればどうなりますか? 0日0時間2分かかりますか? – scsimon

答えて

1

これは少し複雑ですが、ひどいものではありません。これを解決する方法は、文字列からDay(s)、Hours(s)、Minute(s)、Second(s)ラベルを削除し、それらを区切り文字列/文字に置き換えることです。

次に、結果の値をCTEで分割できます。

次に、区切られた文字列内の位置に基づいて、各部分から秒単位でタイムパンを作成します。

最後に、結果のtime-span-part-secondsを合計します。

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

ご返信ありがとうございます。どのように私は期間を渡してint秒を返すことができる関数(例えばcalcSecs)を作成するでしょうか? GROUP BY ID' – Zachafer

+0

このコードにバグがありました(秒が指定されていない秒数でカウントされます) 。 - 一定! ここで関数の使用をお勧めしません。関数は単一のスレッドプロセスであり、クエリが遅くなります。 可能であれば、このロジックをストアドプロシージャで使用することをお勧めします。 – mrtig

+0

とにかく機能が必要な場合に備え、関数の形になるように答えを更新しました。 – mrtig

0

別のオプション望ましい結果を得るためにsubstringpatindexと共通テーブル式のカップルを使用することです。

まず、サンプルテーブルを作成してデータを入力します。(Please

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結果を保持するために1つのCTEを使用します)私達にあなたの将来の質問では、このステップを保存します。
これは、patindexを何度も繰り返し書き込む必要がないためです。更新、今

;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 

が更新を確認する 最初のCTEを使用して、各時間部分の値を抽出するsubstringを使用するこれに別のCTEを追加結果:

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 
関連する問題