5

次のような MS SQL 2008 データベース テーブルがあります。

Registration | Date | DriverID | TrailerID

一部のデータがどのように見えるかの例は次のとおりです。

AB53EDH,2013/07/03 10:00,54,23
AB53EDH,2013/07/03 10:01,54,23
...
AB53EDH,2013/07/03 10:45,54,23
AB53EDH,2013/07/03 10:46,54,NULL <-- Trailer changed
AB53EDH,2013/07/03 10:47,54,NULL
...
AB53EDH,2013/07/03 11:05,54,NULL
AB53EDH,2013/07/03 11:06,54,102  <-- Trailer changed
AB53EDH,2013/07/03 11:07,54,102
...
AB53EDH,2013/07/03 12:32,54,102
AB53EDH,2013/07/03 12:33,72,102  <-- Driver changed
AB53EDH,2013/07/03 12:34,72,102

ご覧のとおり、データは、どの時点でどのドライバーとどのトレーラーがどの登録に関連付けられていたかを表しています。私がやりたいことは、ドライバーとトレーラーの各組み合わせがアクティブだった期間を含むレポートを生成することです。したがって、上記のサンプル データの場合、次のようなものを生成したいと思います。

Registration,StartDate,EndDate,DriverID,TrailerID
AB53EDH,2013/07/03 10:00,2013/07/03 10:45,54,23
AB53EDH,2013/07/03 10:46,2013/07/03 11:05,54,NULL
AB53EDH,2013/07/03 11:06,2013/07/03 12:32,54,102
AB53EDH,2013/07/03 12:33,2013/07/03 12:34,72,102

これを SQL 経由で行うにはどうすればよいでしょうか。

更新:これまでの回答に感謝します。残念ながら、私が持っている本番データに適用すると、機能しなくなりました。これまでに送信されたクエリは、データの一部に適用すると正しく機能しません。

データ テーブルを生成し、上記のダミー データを入力するサンプル クエリを次に示します。ここには、上記の例よりも多くのデータがあります。ドライバーとトレーラーの組み合わせ 54、23、および 54、NULL が繰り返され、これらが 2 つの異なるグループであることをクエリが認識できるようになっています。また、データセットの一部で実行したときにクエリが機能するかどうかをテストするために、同じデータを異なる日付範囲で 3 回レプリケートしました。

CREATE TABLE [dbo].[TempTable](
    [Registration] [nvarchar](50) NOT NULL,
    [Date] [datetime] NOT NULL,
    [DriverID] [int] NULL,
    [TrailerID] [int] NULL
)

INSERT INTO dbo.TempTable
VALUES 
('AB53EDH','2013/07/03 10:00', 54,23),
('AB53EDH','2013/07/03 10:01', 54,23),
('AB53EDH','2013/07/03 10:45', 54,23),
('AB53EDH','2013/07/03 10:46', 54,NULL),
('AB53EDH','2013/07/03 10:47', 54,NULL),
('AB53EDH','2013/07/03 11:05', 54,NULL),
('AB53EDH','2013/07/03 11:06', 54,102),
('AB53EDH','2013/07/03 11:07', 54,102),
('AB53EDH','2013/07/03 12:32', 54,102),
('AB53EDH','2013/07/03 12:33', 72,102),
('AB53EDH','2013/07/03 12:34', 72,102),
('AB53EDH','2013/07/03 13:00', 54,102),
('AB53EDH','2013/07/03 13:01', 54,102),
('AB53EDH','2013/07/03 13:02', 54,102),
('AB53EDH','2013/07/03 13:03', 54,102),
('AB53EDH','2013/07/03 13:04', 54,23),
('AB53EDH','2013/07/03 13:05', 54,23),
('AB53EDH','2013/07/03 13:06', 54,23),
('AB53EDH','2013/07/03 13:07', 54,NULL),
('AB53EDH','2013/07/03 13:08', 54,NULL),
('AB53EDH','2013/07/03 13:09', 54,NULL),
('AB53EDH','2013/07/03 13:10', 54,NULL),
('AB53EDH','2013/07/03 13:11', NULL,NULL)

INSERT INTO dbo.TempTable
SELECT Registration, DATEADD(M, -1, Date), DriverID, TrailerID
FROM dbo.TempTable
WHERE Date > '2013/07/01'

INSERT INTO dbo.TempTable
SELECT Registration, DATEADD(M, 1, Date), DriverID, TrailerID
FROM dbo.TempTable
WHERE Date > '2013/07/01'
4

3 に答える 3

1

相関サブクエリを使用するアプローチは次のとおりです。

with tt as (
       select tt.*,
              (select top 1 date
               from TempTable tt2
               where tt2.Registration = tt.Registration and
                     tt2.DriverID = tt.DriverID and
                     (tt2.TrailerID = tt.TrailerID or tt2.TrailerID is null and tt.TrailerID is null) and
                     tt2.Date < tt.Date
               order by date desc
              ) prevDate
       from TempTable tt
      )
select registration, min(date) as startdate, max(date) as enddate, driverid, trailerid
from (select tt.*,
             (select top 1 date
              from tt tt3
              where prevDate is NULL and
                    tt3.Date <= tt.date
              order by Date desc
             ) as grp
      from TempTable tt
     ) tt
group by grp, Registration, DriverID, trailerid;

CTE はlag(date)、登録、ドライバー ID、およびトレーラー ID に対して実行し、レコードの前の日付を生成します。これはNULL一連のレコードの先頭にあります。

次に、サブクエリは、特定のレコードまたはその前の NULL レコードで最新の日付を検索します。これは、グループ化変数として機能します。grpこの時点では、シーケンス内のすべてが同じです。

最後のクエリは、これを必要な形式に集約します。

これは複雑なクエリです。lag()SQL Server 2012 では、累積集計関数とを使用して、構文をいくらか簡略化できます。これらの関数を使用すると、基本的に同じアプローチに従うことができます。

編集:

ああ。上記のクエリには、前の日付の計算に論理エラーがあります。この修正では、日付がデータ内で一意であると想定する必要があります。

上記のエラーは、列のトリプルが一致する前の日付を探していることです。ばか、ばか、ばか。一致するがデータの前にあるトリプルが存在する可能性があるためです。代わりに、前の日付を取得してから、トリプルが一致するかどうかを確認する必要があります。

以下は、追加の結合でこれを実装します。ここでは SQL Fiddleで実行されています。

with tt as (
       select tt.*, tt3.date as PrevDate
       from (select tt.*,
                    (select top 1 date
                     from TempTable tt2
                     where tt2.date < tt.date
                     order by date desc
                   ) prevDate1
             from TempTable tt
            ) tt left outer join
            TempTable tt3
            on tt.prevdate1 = tt3.date and
               tt3.Registration = tt.Registration and
               tt3.DriverID = tt.DriverID and
               (tt3.TrailerID = tt.TrailerID or tt3.TrailerID is null and tt.TrailerID is null)
     )
select registration, count(*), min(date) as startdate, max(date) as enddate, driverid, trailerid
from (select tt.*,
             (select top 1 date
              from tt tt3
              where prevDate is NULL and
                    tt3.Date <= tt.date
              order by Date desc
             ) as grp
      from TempTable tt
     ) tt
group by grp, Registration, DriverID, trailerid;
于 2013-07-03T13:19:43.253 に答える
1

試す-:

DECLARE @TempTable AS TABLE (
    [Registration] [nvarchar](50) NOT NULL,
    [Date] [datetime] NOT NULL,
    [DriverID] [int] NULL,
    [TrailerID] [int] NULL
)

INSERT INTO @TempTable
VALUES 
('AB53EDH','2013-07-03 10:00', 54,23),
('AB53EDH','2013-07-03 10:01', 54,23),
('AB53EDH','2013-07-03 10:45', 54,23),
('AB53EDH','2013-07-03 10:46', 54,nULL),
('AB53EDH','2013-07-03 10:47', 54,NULL),
('AB53EDH','2013-07-03 11:05', 54,NULL),
('AB53EDH','2013-07-03 11:06', 54,102),
('AB53EDH','2013-07-03 11:07', 54,102),
('AB53EDH','2013-07-03 12:32', 54,102),
('AB53EDH','2013-07-03 12:33', 72,102),
('AB53EDH','2013-07-03 12:34', 72,102)

SELECT t1.Registration, MIN(t1.Date) AS StartDate, MAX(t1.date) AS EndDate, t1.DriverID, t1.TrailerID
FROM @TempTable AS t1
INNER JOIN @TempTable AS t2
    ON t1.Registration = t2.Registration AND (t1.DriverID = t2.DriverID OR t1.TrailerID = t2.TrailerID)
GROUP BY t1.Registration, t1.DriverID, t1.TrailerID
    ORDER BY MIN(t1.Date)
于 2013-07-03T13:16:41.940 に答える