4

日付範囲に基づいていくつかのデータ行を組み合わせる必要があるクエリに取り組んでいます。これらの行は、日付範囲が分割されている場合を除き、すべてのデータ値で重複しています。たとえば、テーブル データは次のようになります。

StudentID   StartDate   EndDate     Field1  Field2
1           9/3/2007    10/20/2007  3       True
1           10/21/2007  6/12/2008   3       True
2           10/10/2007  3/20/2008   4       False
3           9/3/2007    11/3/2007   8       True
3           12/15/2007  6/12/2008   8       True

クエリの結果には、分割された日付範囲が結合されている必要があります。クエリは、日付範囲を 1 日のみのギャップで結合する必要があります。1 日以上のギャップがある場合は、行を結合しないでください。分割された日付範囲を持たない行は変更されないはずです。結果は次のようになります

StudentID   StartDate   EndDate     Field1  Field2
1           9/3/2007    6/12/2008   3       True
2           10/10/2007  3/20/2008   4       False
3           9/3/2007    11/3/2007   8       True
3           12/15/2007  6/12/2008   8       True

このクエリの SELECT ステートメントは何ですか?

4

10 に答える 10

2

次のコードは機能するはずです。私は次のようにいくつかの仮定を立てました: 日付範囲の重複がないこと、どのフィールドにも NULL 値がないこと、および特定の行の開始日が常に終了日より前であること。データがこれらの基準に適合しない場合は、この方法を調整する必要がありますが、正しい方向に向ける必要があります。

ビューの代わりにサブクエリを使用することもできますが、これは面倒なので、コードをわかりやすくするためにビューを使用しました。

CREATE VIEW dbo.StudentStartDates
AS
    SELECT
        S.StudentID,
        S.StartDate,
        S.Field1,
        S.Field2
    FROM
        dbo.Students S
    LEFT OUTER JOIN dbo.Students PREV ON
        PREV.StudentID = S.StudentID AND
        PREV.Field1 = S.Field1 AND
        PREV.Field2 = S.Field2 AND
        PREV.EndDate = DATEADD(dy, -1, S.StartDate)
    WHERE PREV.StudentID IS NULL
GO

CREATE VIEW dbo.StudentEndDates
AS
    SELECT
        S.StudentID,
        S.EndDate,
        S.Field1,
        S.Field2
    FROM
        dbo.Students S
    LEFT OUTER JOIN dbo.Students NEXT ON
        NEXT.StudentID = S.StudentID AND
        NEXT.Field1 = S.Field1 AND
        NEXT.Field2 = S.Field2 AND
        NEXT.StartDate = DATEADD(dy, 1, S.EndDate)
    WHERE NEXT.StudentID IS NULL
GO


SELECT
    SD.StudentID,
    SD.StartDate,
    ED.EndDate,
    SD.Field1,
    SD.Field2
FROM
    dbo.StudentStartDates SD
INNER JOIN dbo.StudentEndDates ED ON
    ED.StudentID = SD.StudentID AND
    ED.Field1 = SD.Field1 AND
    ED.Field2 = SD.Field2 AND
    ED.EndDate > SD.StartDate AND
    NOT EXISTS (SELECT * FROM dbo.StudentEndDates ED2 WHERE ED2.StudentID = SD.StudentID AND ED2.Field1 = SD.Field1 AND ED2.Field2 = SD.Field2 AND ED2.EndDate < ED.EndDate AND ED2.EndDate > SD.StartDate)
GO
于 2008-09-26T16:38:16.943 に答える
0

受け入れられた回答で Tom H. によって提供されたものに対する代替の最終クエリは次のとおりです。

SELECT
    SD.StudentID,
    SD.StartDate,
    MIN(ED.EndDate),
    SD.Field1,
    SD.Field2
FROM
    dbo.StudentStartDates SD
INNER JOIN dbo.StudentEndDates ED ON
    ED.StudentID = SD.StudentID AND
    ED.Field1 = SD.Field1 AND
    ED.Field2 = SD.Field2 AND
    ED.EndDate > SD.StartDate
GROUP BY
    SD.StudentID, SD.Field1, SD.Field2, SD.StartDate

これは、すべてのテストデータでも機能しました。

于 2008-09-26T19:56:31.897 に答える
0

私の経験では、後処理で範囲を結合する必要があります (SQL ではなくスクリプトで)。特に、特定のケースでいくつの日付範囲をチェーンする必要があるかを正確に知ることはできないため、SQLでこれを実行できるかどうかはわかりません. もしこれができるなら、私も知りたいです。

編集:私の答えは、開始と終了だけでなく、学生ごとに複数の日付範囲があることを前提としています。ギャップのない日付範囲が 1 つしかない場合は、言及されている他の解決策が最適です。

于 2008-09-26T15:28:25.350 に答える
0
SELECT StudentID, MIN(startdate) AS startdate, MAX(enddate), field1, field2
FROM tablex
GROUP BY StudentID, field1, field2

学生の時間範囲の間にギャップがなかったと仮定すると、結果が得られます。

于 2008-09-26T15:28:58.483 に答える
0
select StudentID, min(StartDate) StartDate, max(EndDate) EndDate, Field1, Field2 
  from table
 group by StudentID, Field1, Field2
于 2008-09-26T15:28:59.093 に答える
0

SQL Server 2005/2008 構文を使用したテスト データの例を次に示します。

DECLARE @Data TABLE(
    CalendarDate datetime )

INSERT INTO @Data( CalendarDate )
-- range start
SELECT '1 Jan 2010'
UNION ALL SELECT '2 Jan 2010'
UNION ALL SELECT '3 Jan 2010'
-- range start
UNION ALL SELECT '5 Jan 2010'
-- range start
UNION ALL SELECT '7 Jan 2010'
UNION ALL SELECT '8 Jan 2010'
UNION ALL SELECT '9 Jan 2010'
UNION ALL SELECT '10 Jan 2010'

SELECT DateGroup, Min( CalendarDate ) AS StartDate, Max( CalendarDate ) AS EndDate
FROM(   SELECT NextDay.CalendarDate, 
            DateDiff( d, RangeStart.CalendarDate, NextDay.CalendarDate ) - ROW_NUMBER() OVER( ORDER BY NextDay.CalendarDate ) AS DateGroup
        FROM( SELECT Min( CalendarDate ) AS CalendarDate
                FROM @data ) AS RangeStart
            JOIN @data AS NextDay
                ON NextDay.CalendarDate >= RangeStart.CalendarDate ) A
GROUP BY DateGroup
于 2010-05-20T22:12:47.330 に答える
0

min()/max() ソリューションが十分でない場合 (たとえば、日付が連続しておらず、別々の日付範囲を個別にグループ化したい場合)、Oracle の START WITH および CONNECT BY 句を使用する何かが機能するかどうか疑問に思います。もちろん、これはすべてのデータベースで機能するわけではありません。

于 2008-09-26T16:06:41.093 に答える
0

編集: Access 用の SQL の別のセットを作成します。私はこれらすべてをテストしましたが、Access で一度に複数のステートメントを作成する方法がわからないため、1 つずつテストしました。私もコメントのやり方がよくわからないので、下の SQL バージョンでコメントを確認できます。

select 
studentid, min(startdate) as Starter, max(enddate) as Ender, field1, field2, 
max(startDate) - Min(endDate)  as MaxGap 
into tempIDs
from student 
group by studentid, field1, field2 ;  

delete from tempIDs where MaxGap > 1;

UPDATE student INNER JOIN TempIDs ON Student.studentID = TempIDS.StudentID
SET Student.StartDate = [TempIDs].[Starter],
 Student.EndDate = [TempIDs].[Ender];

SQL Serverではこれだと思います-Accessでは実行しませんでした。複数のレコードのオーバーラップなどの特殊な条件についてはテストしていませんが、これで始められるはずです。重複する小さなギャップ レコードをすべて更新し、余分なものをデータベースに残します。MSDN には、重複の排除に関するページがあります: http://support.microsoft.com/kb/139444

select 
studentid, min(startdate) as StartDate, max(enddate) as EndDate, field1, field2, 
datediff(dd, Min(endDate),max(startDate)) as MaxGap 
into #tempIDs
from #student 
group by studentid, field1, field2    

-- Update the relevant records.  Keeps two copies of the massaged record 
-- - extra will need to be deleted.

update #student 
set startdate = #TempIDS.startdate, enddate = #tempIDS.EndDate
from #tempIDS 
where #student.studentid = #TempIDs.StudentID and MaxGap < 2
于 2008-09-26T16:19:38.753 に答える
0

非等価結合を検討しましたか? それは次のようになります。

SELECT A.StudentID, A.StartDate, A.EndDate, A.Field1, A.Field2
FROM tblEnrollment AS A LEFT JOIN tblEnrollment AS B ON (A.StudentID = B.StudentID) 
   AND (A.EndDate=B.StartDate-1)
WHERE B.StudentID Is Null;

これにより、最初のレコードの終了日の翌日から始まる、対応するレコードを持たないすべてのレコードが得られます。

[注意: SQL ビューの Access クエリ デザイナーでのみ非等価結合を編集できることに注意してください。デザイン ビューに切り替えると、結合が失われる可能性があります (ただし、Access に切り替えると、問題について通知されます。すぐに SQL ビューに切り替えても、失われることはありません)]

次に、これをUNIONする場合:

SELECT A.StudentID, A.StartDate, B.EndDate, A.Field1, A.Field2
FROM tblEnrollment AS A INNER JOIN tblEnrollment AS B ON (A.StudentID = B.StudentID) 
   AND (A.EndDate= B.StartDate-1)

一度に 2 つ以上の連続したレコードが存在しないと仮定すると、必要なものが得られるはずです。2 つ以上の連続したレコードがある場合 (StartDate-1 と EndDate を比較する必要があるかもしれません)、どうすればよいかわかりませんが、これで正しい方向に進むことができるかもしれません。

于 2008-09-26T19:31:45.843 に答える
-1

これは、SQL (言語) の古典的な問題です。たとえば、Joe Celko の書籍「SQL for Smarties」(第 23 章、領域、実行、ギャップ、シーケンス、およびシリーズ) と、彼の最新の書籍「Thinking in Sets」(第 15 章) で説明されています。

モンスター クエリを使用して実行時にデータを修正するのは「楽しい」ことですが、私にとって、これはオフラインで手続き的に修正する方が適切な状況の 1 つです (個人的には、Excel スプレッドシートの数式を使用して修正します)。

重要なことは、有効なデータベース制約を設定して、期間の重複が再発しないようにすることです。繰り返しになりますが、SQL でシーケンス制約を記述することは古典的です。Snodgrass ( http://www.cs.arizona.edu/people/rts/tdbbook.pdf ) を参照してください。MS Access ユーザーへのヒント: CHECK 制約を使用する必要があります。

于 2008-09-29T12:36:17.953 に答える