1

3列 (id(int),date(date),Status(bool)) のテーブルがあります。

このような

id  date        Status
1   2012-10-18  1
1   2012-10-19  1
1   2012-10-20  0
1   2012-10-21  0
1   2012-10-22  0
1   2012-10-23  0
1   2012-10-24  1
1   2012-10-25  0
1   2012-10-26  0
1   2012-10-27  0
1   2012-10-28  1
2   2012-10-19  0
2   2012-10-20  0
2   2012-10-21  0
2   2012-10-22  1
2   2012-10-23  1

日付列は連続しており、日付間にギャップはないと仮定します。

連続する 3 つのゼロ ([ステータス] 列) とその翌日のステータスを確認するにはどうすればよいですか?

このような

id  startDate     endDate       NextDayStatus
1   2012-10-20    2012-10-22         0
1   2012-10-21    2012-10-23         1
1   2012-10-25    2012-10-27         1
2   2012-10-19    2012-10-21         1

テーブル作成スクリプトとサンプルデータ

CREATE TABLE [Table1](
    [ID] [smallint] NOT NULL,
    [Date] [date] NOT NULL,
    [Status] [bit] NULL,
 CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED  (  [ID] ASC,   [Date] ASC ) )

INSERT INTO [Table1]([ID], [Date], [Status])     
SELECT 1, '2012-10-18', 1    UNION ALL
SELECT 1, '2012-10-19', 1    UNION ALL
SELECT 1, '2012-10-20', 0    UNION ALL
SELECT 1, '2012-10-21', 0    UNION ALL
SELECT 1, '2012-10-22', 0    UNION ALL
SELECT 1, '2012-10-23', 0    UNION ALL
SELECT 1, '2012-10-24', 1    UNION ALL 
SELECT 1, '2012-10-25', 0    UNION ALL
SELECT 1, '2012-10-26', 0    UNION ALL
SELECT 1, '2012-10-27', 0    UNION ALL
SELECT 1, '2012-10-28', 1    UNION ALL
SELECT 2, '2012-10-19', 0    UNION ALL
SELECT 2, '2012-10-20', 0    UNION ALL
SELECT 2, '2012-10-21', 0    UNION ALL
SELECT 2, '2012-10-22', 1    UNION ALL
SELECT 2, '2012-10-23', 1

アップデート:

  • 重要な場合は、この手順の後、月の最初、10 日、または 20 日を除外するだけで済みます。
  • Tomalak と gnb に感謝します。私の実際のタスクでは、連続するゼロの数は、このサンプルでは 3 ではなく9であるため、9 つの内部結合またはクロス適用を使用すると効率が悪いようです。
4

3 に答える 3

4

編集、ID パーティショニング用に更新

これは、日付が連続していない場合にも機能します

SELECT        T1.id, T1.[Date], MAX(X.[Date]), Y.[Status]
FROM     Table1 T1       
   CROSS APPLY
   (  SELECT TOP 3 *
   FROM            Table1 T2
   WHERE           T2.id = T1.id AND T2.[Date] >= T1.Date
   ORDER BY        T2.[Date]
   ) X
   CROSS APPLY
   ( SELECT TOP 4 *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY T3.[Date]) AS rn
   FROM            Table1 T3
   WHERE           T3.id = T1.id AND T3.[Date] >= T1.Date
   ORDER BY        T3.[Date]
   ) Y
WHERE        y.rn = 4
GROUP BY     T1.id, T1.[Date], Y.[Status]
HAVING       SUM(CAST(X.[Status] AS tinyint)) = 0;

完全を期すために、これはより洗練された SQL Server 2012 ソリューションの方法です。これは、
適切なウィンドウ処理/分析サポートを備えた任意の RDBMS で使用できます。

SELECT
    X.id, X.startDate, X.endDate, x.nextStatus
FROM
    ( SELECT        T1.id, T1.[Date] AS startDate,
        LEAD(T1.[Date], 2) OVER (PARTITION BY T1.id ORDER BY T1.[Date]) AS endDate,
        LEAD(T1.[Status], 3) OVER (PARTITION BY T1.id ORDER BY T1.[Date]) AS nextStatus,
        SUM(CAST(T1.[Status] AS tinyint)) OVER (PARTITION BY T1.id ORDER BY T1.[Date] ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS SumNext3
    FROM            Table1 T1
    ) X
WHERE        SumNext3 = 0;
于 2013-06-20T07:48:09.980 に答える
3
SELECT
  z1.id, z1.[date] AS startDate ,z3.[date] AS endDate, zn.status AS NextDayStatus
FROM 
  Table1 z1
  INNER JOIN Table1 z2 ON z2.[date] = (
    SELECT MIN([date]) FROM Table1 WHERE [date] > z1.[date] AND id = z1.id
  )
  INNER JOIN Table1 z3 ON z3.Date = (
    SELECT MIN([date]) FROM Table1 WHERE [date] > z2.[date] AND id = z1.id
  )
  INNER JOIN Table1 zn ON zn.Date = (
    SELECT MIN([date]) FROM Table1 WHERE [date] > z3.[date] AND id = z1.id
  )
WHERE 
  z1.status = 0
  AND z2.status = 0 AND z2.id = z1.id
  AND z3.status = 0 AND z3.id = z1.id
  AND zn.id = z1.id
ORDER BY
  z1.id, z1.[date]

Table1 のインデックス(date, status, id)が最適です。

于 2013-06-20T07:51:44.080 に答える
2

多くの SQL 製品 (ウィンドウ関数をサポートする製品) でも機能する別のソリューションを次に示しますが、特に SQL Server 2005 以降のバージョンでは次のようになります。

WITH partitioned AS (
  SELECT
    *,
    grp = DATEDIFF(DAY, 0, Date)
        - ROW_NUMBER() OVER (PARTITION BY ID, Status ORDER BY Date)
  FROM Table1
),
grouped AS (
  SELECT
    ID,
    SD = MIN(Date),
    ED = MAX(Date)
  FROM partitioned
  WHERE Status = 0
  GROUP BY
    ID,
    grp
  HAVING COUNT(*) >= 3
)
SELECT
  t.ID,
  StartDate     = t.Date,
  EndDate       = DATEADD(DAY, 2, t.Date),
  NextDayStatus = CASE t.Date WHEN DATEADD(DAY, -2, g.ED) THEN 1 ELSE 0 END
FROM Table1 t
INNER JOIN grouped g
ON t.ID = g.ID AND t.Date BETWEEN g.SD AND DATEADD(DAY, -2, g.ED)
;

のすべての「島」を検出し、Status = 0少なくとも 3 つの行があるものを選択してから、集約された島セットを元のテーブルに結合して、3 つの連続するStatus = 0行の必要なサブセットの開始となる資格のある行を取得します。

ただし、このソリューションでは、3 つの連続するステータス 0 行の後に、同じ ID を持つ他の行が少なくとも 1 行続くことを前提としています。言い換えれば、ステータス 0 行の最後の一致セットの後にステータス 1 行が続くはずです。

于 2013-06-25T06:48:30.193 に答える