2017-03-27 14 views
2

ビットをオン/オフに設定した時間の要約を取得するクエリを作成する必要があります。TSQL - 条件付きの時間列の合計

例:

╔═══════════════════════════╗ 
║   TABLE   ║ 
╠════╦════════╦═════╦═══════╣ 
║ ID ║ TIME ║ BIT ║ VALUE ║ 
╠════╬════════╬═════╬═══════╣ 
║ 1 ║ 13:40 ║ 1 ║ 5 ║ 
║ 2 ║ 13:45 ║ 1 ║ 3 ║ 
║ 3 ║ 13:50 ║ 1 ║ 1 ║ 
║ 4 ║ 13:55 ║ 0 ║ 2 ║ 
║ 5 ║ 14:00 ║ 0 ║ 7 ║ 
║ 6 ║ 14:05 ║ 1 ║ 3 ║ 
║ 7 ║ 14:10 ║ 1 ║ 4 ║ 
║ 8 ║ 14:15 ║ 0 ║ 2 ║ 
║ 9 ║ 14:20 ║ 1 ║ 2 ║ 
╚════╩════════╩═════╩═══════╝ 

私は総TIMEの概要(とVALUE - 1シンプルな)を持っていると思いBITSET ONたとき:

13:40 - 13:50 = 10 mins 
14:05 - 14:10 = 5 mins 
14:20   = no end time, 0 mins 
----------------------------------------- 
       15 mins 

が見つけました:

上で動作します処理された最後の日付を渡す再帰関数(最後に処理されたdatetimeを渡す)として実行し、BITONであるためdatetimeを合計します。 VALUE(簡単なもの)加算する

SQLクエリ:BITあった時に

SELECT SUM(Value) 
FROM Table 
WHERE Bit = 1 

がどのように私は分(時間)の合計値を取得する必要がありますが、ONを設定しますか?


EDIT:テストのために使用することができますクエリ:

DECLARE @Table TABLE(
    ID INT Identity(1,1) PRIMARY KEY, 
    [TIME] DATETIME NOT NULL, 
    [BIT] BIT NOT NULL, 
    [VALUE] INT NOT NULL 
); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('13:40',1,5); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('13:45',1,3); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('13:50',1,1); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('13:55',0,2); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('14:00',0,7); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('14:05',1,3); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('14:10',1,4); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('14:15',0,2); 
INSERT INTO @Table([TIME],[BIT],[VALUE]) VALUES('14:20',1,2); 
SELECT * FROM @Table; 

答えて

2

LEAD関数を使用して、次の行の時間を取得し、時間間隔を計算します。結果を[ビット]でグループ化してください

WITH t AS(
SELECT 
    [time], 
    DATEDIFF(minute, [time], LEAD([time], 1, null) OVER (ORDER BY [time])) AS interval, 
    [bit], 
    [value] 
FROM table1) 
SELECT [bit], CAST(DATEADD(MINUTE, SUM(interval), '00:00') AS TIME), SUM([value]) FROM t 
GROUP BY [bit] 
+0

これは本当に読みやすく、わかりやすいものです。答えをありがとう! – Tatranskymedved

+0

update:(BY [id])ではな​​く、OVER(ORDER BY [時間])を使用します。 Idはシーケンシャルである必要はないからです。それはguidまたはnatural keyかもしれません – usart

2

次の2つの問題があります。時間を合計し、隣接する値を識別します。あなたは行番号の違いのアプローチで2番目を処理することができます。あなたは数分に変換することによって、元を扱うことができます。

select bit, min(time), max(time), 
     sum(datediff(minute, 0, time)) as minutes, 
     sum(value) 
from (select t.*, 
      row_number() over (order by id) as seqnum, 
      row_number() over (partition by bit order by id) as seqnum_b 
     from t 
    ) t 
group by (seqnum - seqnum_b), bit; 
+0

ありがとうございます!しかし、距離(Seqnum-seqnum_b)がより多くの場合同じようになる場合に問題があることがわかります。集計関数に間違った結果が出るでしょう.-「MIN」が最初に起こり、「MAX」が最後のものを取るでしょう。 – Tatranskymedved

+0

@Tatranskymedved。 。 。これを試しましたか?これは隣接する行を見つけるために働きます。おそらく、あなたは 'グループで 'のビットを放棄したでしょう。 –

+0

TBHは試していませんが、今テストしました。これはスマートなコードです!アイデアをありがとう、私はこれがこのように行うことができると想像することはありません。とにかくこれは、トピックの完全な答えではなく、それはその一部だけに対処しています。あなたの時間を感謝します! =) – Tatranskymedved

1

は、これはかなり標準溶液を用いて、「ギャップと島」の問題です。私はこれを考え出しました。これはゴードンとほぼ同じですが、間隔を計算する余分なステップがあります。これは本質的に重複している回答を投稿している唯一の理由ですが、実際にはゼロからの差をとることは実際にはうまくいかないのでしょうか?

DECLARE @table TABLE (id int, [time] TIME, [bit] BIT, value INT); 
INSERT INTO @table SELECT 1, '13:40', 1, 5; 
INSERT INTO @table SELECT 2, '13:45', 1, 3; 
INSERT INTO @table SELECT 3, '13:50', 1, 1; 
INSERT INTO @table SELECT 4, '13:55', 0, 2; 
INSERT INTO @table SELECT 5, '14:00', 0, 7; 
INSERT INTO @table SELECT 6, '14:05', 1, 3; 
INSERT INTO @table SELECT 7, '14:10', 1, 4; 
INSERT INTO @table SELECT 8, '14:15', 0, 2; 
INSERT INTO @table SELECT 9, '14:20', 1, 2; 
WITH x AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY [bit] ORDER BY id) AS a_id, ROW_NUMBER() OVER (ORDER BY id) AS b_id FROM @table), 
y AS (
    SELECT [bit], MIN([time]) AS min_time, MAX([time]) AS max_time, SUM(value) AS value FROM x GROUP BY a_id - b_id, [bit]) 
SELECT [bit], SUM(value) AS total_value, SUM(DATEDIFF(MINUTE, min_time, max_time)) AS total_minutes FROM y GROUP BY [bit]; 

結果:

ボーナスとして

bit total_value total_minutes 
0 11   5 
1 18   15 

ここでの唯一のビットが1に設定されている場合、経過時間があるどのくらいの実際の問題、すなわちを解決ソリューションです。

WITH x AS (SELECT id, id - DENSE_RANK() OVER(ORDER BY id) AS grp FROM @table WHERE [bit] = 1), y AS (SELECT MIN(id) AS range_start, MAX(id) AS range_end FROM x GROUP BY grp) 
SELECT SUM(DATEDIFF(MINUTE, t1.[time], t2.[time])) AS minutes_elapsed FROM y INNER JOIN @table t1 ON t1.id = y.range_start INNER JOIN @table t2 ON t2.id = y.range_end; 
+0

更新後、私はそれがどのように動作するかをはっきりと見ることができます。問題は最初のクエリを理解できました= /回答ありがとうございます! – Tatranskymedved