คำนวณจำนวนการเข้าชมทั้งหมด


12

ฉันกำลังพยายามเขียนแบบสอบถามที่ฉันต้องคำนวณจำนวนการเข้าชมสำหรับลูกค้าโดยดูแลวันที่ทับซ้อนกัน สมมติว่าสำหรับวันที่เริ่มต้น itemID 2009 คือ 23 และวันที่สิ้นสุดคือ 26 ดังนั้นรายการ 20010 อยู่ระหว่างวันเหล่านี้เราจะไม่เพิ่มวันที่ซื้อนี้ไปยังจำนวนรวมของเรา

ตัวอย่างสถานการณ์:

Item ID Start Date   End Date   Number of days     Number of days Candidate for visit count
20009   2015-01-23  2015-01-26     4                      4
20010   2015-01-24  2015-01-24     1                      0
20011   2015-01-23  2015-01-26     4                      0
20012   2015-01-23  2015-01-27     5                      1
20013   2015-01-23  2015-01-27     5                      0
20014   2015-01-29  2015-01-30     2                      2

OutPut ควรเป็น 7 VisitDays

ตารางอินพุต:

CREATE TABLE #Items    
(
CustID INT,
ItemID INT,
StartDate DATETIME,
EndDate DATETIME
)           


INSERT INTO #Items
SELECT 11205, 20009, '2015-01-23',  '2015-01-26'  
UNION ALL 
SELECT 11205, 20010, '2015-01-24',  '2015-01-24'    
UNION ALL  
SELECT 11205, 20011, '2015-01-23',  '2015-01-26' 
UNION ALL  
SELECT 11205, 20012, '2015-01-23',  '2015-01-27'  
UNION ALL  
SELECT 11205, 20012, '2015-01-23',  '2015-01-27'   
UNION ALL  
SELECT 11205, 20012, '2015-01-28',  '2015-01-29'  

ฉันลองมาแล้ว:

CREATE TABLE #VisitsTable
    (
      StartDate DATETIME,
      EndDate DATETIME
    )

INSERT  INTO #VisitsTable
        SELECT DISTINCT
                StartDate,
                EndDate
        FROM    #Items items
        WHERE   CustID = 11205
        ORDER BY StartDate ASC

IF EXISTS (SELECT TOP 1 1 FROM #VisitsTable) 
BEGIN 


SELECT  ISNULL(SUM(VisitDays),1)
FROM    ( SELECT DISTINCT
                    abc.StartDate,
                    abc.EndDate,
                    DATEDIFF(DD, abc.StartDate, abc.EndDate) + 1 VisitDays
          FROM      #VisitsTable abc
                    INNER JOIN #VisitsTable bc ON bc.StartDate NOT BETWEEN abc.StartDate AND abc.EndDate      
        ) Visits

END



--DROP TABLE #Items 
--DROP TABLE #VisitsTable      

คำตอบ:


5

แบบสอบถามแรกนี้สร้างช่วงวันที่เริ่มต้นและวันที่สิ้นสุดที่แตกต่างกันโดยไม่มีการทับซ้อน

บันทึก:

  • ตัวอย่างของคุณ ( id=0) ผสมกับตัวอย่างจาก Ypercube ( id=1)
  • วิธีแก้ปัญหานี้อาจปรับขนาดได้ไม่ดีกับข้อมูลจำนวนมากสำหรับแต่ละ id หรือจำนวนมากของ id นี่เป็นข้อดีของการไม่ต้องใช้ตารางตัวเลข ด้วยชุดข้อมูลขนาดใหญ่ตารางตัวเลขน่าจะให้ประสิทธิภาพที่ดีขึ้น

ค้นหา:

SELECT DISTINCT its.id
    , Start_Date = its.Start_Date 
    , End_Date = COALESCE(DATEADD(day, -1, itmax.End_Date), CASE WHEN itmin.Start_Date > its.End_Date THEN itmin.Start_Date ELSE its.End_Date END)
    --, x1=itmax.End_Date, x2=itmin.Start_Date, x3=its.End_Date
FROM @Items its
OUTER APPLY (
    SELECT Start_Date = MAX(End_Date) FROM @Items std
    WHERE std.Item_ID <> its.Item_ID AND std.Start_Date < its.Start_Date AND std.End_Date > its.Start_Date
) itmin
OUTER APPLY (
    SELECT End_Date = MIN(Start_Date) FROM @Items std
    WHERE std.Item_ID <> its.Item_ID+1000 AND std.Start_Date > its.Start_Date AND std.Start_Date < its.End_Date
) itmax;

เอาท์พุท:

id  | Start_Date                    | End_Date                      
0   | 2015-01-23 00:00:00.0000000   | 2015-01-23 00:00:00.0000000   => 1
0   | 2015-01-24 00:00:00.0000000   | 2015-01-27 00:00:00.0000000   => 4
0   | 2015-01-29 00:00:00.0000000   | 2015-01-30 00:00:00.0000000   => 2
1   | 2016-01-20 00:00:00.0000000   | 2016-01-22 00:00:00.0000000   => 3
1   | 2016-01-23 00:00:00.0000000   | 2016-01-24 00:00:00.0000000   => 2
1   | 2016-01-25 00:00:00.0000000   | 2016-01-29 00:00:00.0000000   => 5

หากคุณใช้วันที่เริ่มต้นและวันที่สิ้นสุดเหล่านี้กับ DATEDIFF:

SELECT DATEDIFF(day
    , its.Start_Date 
    , End_Date = COALESCE(DATEADD(day, -1, itmax.End_Date), CASE WHEN itmin.Start_Date > its.End_Date THEN itmin.Start_Date ELSE its.End_Date END)
) + 1
...

เอาต์พุต (พร้อมรายการซ้ำ) คือ:

  • 1, 4 และ 2 สำหรับ id 0 (ตัวอย่างของคุณ => SUM=7)
  • 3, 2 และ 5 สำหรับ id 1 (Ypercube sample => SUM=10)

จากนั้นคุณจะต้องใส่ทุกอย่างเข้าด้วยกันSUMและGROUP BY:

SELECT id 
    , Days = SUM(
        DATEDIFF(day, Start_Date, End_Date)+1
    )
FROM (
    SELECT DISTINCT its.id
         , Start_Date = its.Start_Date 
        , End_Date = COALESCE(DATEADD(day, -1, itmax.End_Date), CASE WHEN itmin.Start_Date > its.End_Date THEN itmin.Start_Date ELSE its.End_Date END)
    FROM @Items its
    OUTER APPLY (
        SELECT Start_Date = MAX(End_Date) FROM @Items std
        WHERE std.Item_ID <> its.Item_ID AND std.Start_Date < its.Start_Date AND std.End_Date > its.Start_Date
    ) itmin
    OUTER APPLY (
        SELECT End_Date = MIN(Start_Date) FROM @Items std
        WHERE std.Item_ID <> its.Item_ID AND std.Start_Date > its.Start_Date AND std.Start_Date < its.End_Date
    ) itmax
) as d
GROUP BY id;

เอาท์พุท:

id  Days
0   7
1   10

ข้อมูลที่ใช้กับรหัสที่ต่างกัน 2 รหัส:

INSERT INTO @Items
    (id, Item_ID, Start_Date, End_Date)
VALUES 
    (0, 20009, '2015-01-23', '2015-01-26'),
    (0, 20010, '2015-01-24', '2015-01-24'),
    (0, 20011, '2015-01-23', '2015-01-26'),
    (0, 20012, '2015-01-23', '2015-01-27'),
    (0, 20013, '2015-01-23', '2015-01-27'),
    (0, 20014, '2015-01-29', '2015-01-30'),

    (1, 20009, '2016-01-20', '2016-01-24'),
    (1, 20010, '2016-01-23', '2016-01-26'),
    (1, 20011, '2016-01-25', '2016-01-29')

8

มีคำถามและบทความมากมายเกี่ยวกับช่วงเวลาการบรรจุ ตัวอย่างเช่นช่วงการบรรจุโดย Itzik Ben-Gan

คุณสามารถแพ็คช่วงเวลาของคุณสำหรับผู้ใช้ที่ได้รับ เมื่อบรรจุแล้วจะไม่มีการทับซ้อนดังนั้นคุณสามารถสรุประยะเวลาการบรรจุได้


หากช่วงเวลาของคุณเป็นวันที่โดยไม่มีเวลาฉันจะใช้Calendarตาราง ตารางนี้มีรายชื่อวันที่เป็นเวลาหลายทศวรรษ หากคุณไม่มีตารางปฏิทินให้สร้างขึ้นมาหนึ่งตาราง:

CREATE TABLE [dbo].[Calendar](
    [dt] [date] NOT NULL,
CONSTRAINT [PK_Calendar] PRIMARY KEY CLUSTERED 
(
    [dt] ASC
));

มีหลายวิธีที่จะเติมตารางดังกล่าว

ตัวอย่างเช่นแถว 100K (~ 270 ปี) จาก 1900-01-01:

INSERT INTO dbo.Calendar (dt)
SELECT TOP (100000) 
    DATEADD(day, ROW_NUMBER() OVER (ORDER BY s1.[object_id])-1, '19000101') AS dt
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
OPTION (MAXDOP 1);

ดูเพิ่มเติมเหตุใดตารางตัวเลขจึงประเมินค่าไม่ได้?

เมื่อคุณมีCalendarโต๊ะแล้วนี่คือวิธีการใช้

แต่ละแถวเดิมร่วมกับCalendarตารางที่จะกลับมาเป็นแถวเป็นจำนวนมากมีวันที่ระหว่างและStartDateEndDate

จากนั้นเรานับวันที่แตกต่างกันซึ่งจะลบวันที่ทับซ้อนกัน

SELECT COUNT(DISTINCT CA.dt) AS TotalCount
FROM
    #Items AS T
    CROSS APPLY
    (
        SELECT dbo.Calendar.dt
        FROM dbo.Calendar
        WHERE
            dbo.Calendar.dt >= T.StartDate
            AND dbo.Calendar.dt <= T.EndDate
    ) AS CA
WHERE T.CustID = 11205
;

ผลลัพธ์

TotalCount
7

7

ฉันเห็นด้วยอย่างยิ่งว่า a NumbersและCalendarตารางนั้นมีประโยชน์มากและหากปัญหานี้สามารถทำให้ง่ายขึ้นมากด้วยตารางปฏิทิน

ฉันจะแนะนำวิธีแก้ปัญหาอื่น (ซึ่งไม่จำเป็นต้องมีตารางปฏิทินหรือการรวมหน้าต่าง - เป็นคำตอบบางส่วนจากโพสต์ที่ลิงก์โดย Itzik ทำ) อาจไม่มีประสิทธิภาพสูงสุดในทุกกรณี (หรืออาจเลวร้ายที่สุดในทุกกรณี!) แต่ฉันไม่คิดว่ามันเป็นอันตรายต่อการทดสอบ

มันทำงานโดยการค้นหาวันที่เริ่มต้นและวันที่สิ้นสุดที่ไม่ทับซ้อนกับช่วงเวลาอื่น ๆ แล้ววางลงในสองแถว (แยกจากวันที่เริ่มต้นและสิ้นสุด) เพื่อกำหนดหมายเลขแถวและท้ายที่สุดตรงกับวันที่เริ่มต้น 1 กับวันที่สิ้นสุดที่ 1 , ที่ 2 พร้อมที่ 2 ฯลฯ :

WITH 
  start_dates AS
    ( SELECT CustID, StartDate,
             Rn = ROW_NUMBER() OVER (PARTITION BY CustID 
                                     ORDER BY StartDate)
      FROM items AS i
      WHERE NOT EXISTS
            ( SELECT *
              FROM Items AS j
              WHERE j.CustID = i.CustID
                AND j.StartDate < i.StartDate AND i.StartDate <= j.EndDate 
            )
      GROUP BY CustID, StartDate
    ),
  end_dates AS
    ( SELECT CustID, EndDate,
             Rn = ROW_NUMBER() OVER (PARTITION BY CustID 
                                     ORDER BY EndDate) 
      FROM items AS i
      WHERE NOT EXISTS
            ( SELECT *
              FROM Items AS j
              WHERE j.CustID = i.CustID
                AND j.StartDate <= i.EndDate AND i.EndDate < j.EndDate 
            )
      GROUP BY CustID, EndDate
    )
SELECT s.CustID, 
       Result = SUM( DATEDIFF(day, s.StartDate, e.EndDate) + 1 )
FROM start_dates AS s
  JOIN end_dates AS e
    ON  s.CustID = e.CustID
    AND s.Rn = e.Rn 
GROUP BY s.CustID ;

ดัชนีสองตัวบน(CustID, StartDate, EndDate)และบน(CustID, EndDate, StartDate)จะเป็นประโยชน์สำหรับการปรับปรุงประสิทธิภาพของแบบสอบถาม

ข้อได้เปรียบเหนือปฏิทิน (อาจเป็นเพียงคนเดียว) ก็คือมันสามารถปรับให้ทำงานกับdatetimeค่าและนับความยาวของ "ช่วงเวลาที่บรรจุ" ได้อย่างแม่นยำด้วยความแม่นยำที่แตกต่างกันขนาดใหญ่กว่า (สัปดาห์ปี) หรือเล็กกว่า (ชั่วโมงนาทีหรือวินาที) มิลลิวินาที ฯลฯ ) และไม่เพียง แต่นับวันที่ ตารางปฏิทินที่มีความแม่นยำนาทีหรือวินาทีจะค่อนข้างใหญ่และ (กากบาท) เข้าร่วมกับตารางใหญ่จะเป็นประสบการณ์ที่น่าสนใจ แต่อาจไม่ใช่ตารางที่มีประสิทธิภาพที่สุด

(ขอบคุณ Vladimir Baranov): มันค่อนข้างยากที่จะเปรียบเทียบประสิทธิภาพที่เหมาะสมเนื่องจากประสิทธิภาพของวิธีการที่แตกต่างกันน่าจะขึ้นอยู่กับการกระจายข้อมูล 1) ระยะเวลานานเท่าไหร่ - ยิ่งช่วงเวลาสั้นลงเท่าไหร่ตารางปฏิทินที่ดีกว่าจะทำงานได้เนื่องจากช่วงเวลาที่ยาวนานจะสร้างแถวกลางจำนวนมาก 2) ความถี่ในการทับซ้อนกันบ่อยครั้งเท่าใด - ช่วงเวลาส่วนใหญ่ไม่ทับซ้อนกัน . ฉันคิดว่าประสิทธิภาพของโซลูชันของ Itzik ขึ้นอยู่กับว่า อาจมีวิธีอื่นในการบิดเบือนข้อมูลและเป็นการยากที่จะบอกว่าประสิทธิภาพของวิธีการต่างๆจะได้รับผลกระทบอย่างไร


1
ฉันเห็นสำเนา 2 ชุด หรืออาจเป็น 3 ถ้าเรานับ anti-semijoins เป็น 2 ครึ่ง;)
ypercubeᵀᴹ

1
@wBob หากคุณทำการทดสอบประสิทธิภาพโปรดเพิ่มพวกเขาในคำตอบของคุณ ฉันดีใจที่ได้เห็นพวกเขาและอื่น ๆ อีกมากมายแน่นอน นั่นเป็นวิธีที่เว็บไซต์ทำงาน ..
ypercubeᵀᴹ

3
@wBob ไม่จำเป็นต้องต่อสู้กันมาก - ไม่มีใครแสดงความกังวลเกี่ยวกับประสิทธิภาพ หากคุณมีข้อกังวลใจคุณสามารถทำการทดสอบได้เอง การวัดความคิดเห็นของคุณเกี่ยวกับความซับซ้อนของคำตอบไม่ใช่เหตุผลสำหรับการลงคะแนนเสียง คุณจะทำแบบทดสอบของคุณเองอย่างไรและขยายคำตอบของคุณเองแทนที่จะนำคำตอบอื่นมาลง? ทำให้คำตอบของคุณเองมีค่ามากขึ้นสำหรับการโหวตถ้าคุณต้องการ แต่อย่าลงคะแนนคำตอบที่ถูกกฎหมายอื่น ๆ
Monkpit

1
ฮ่า ๆ ไม่มีการต่อสู้ที่นี่ @ Monkpit เหตุผลที่สมบูรณ์แบบและการสนทนาที่จริงจังเกี่ยวกับประสิทธิภาพ
wBob

2
@ wBob มันค่อนข้างยากที่จะมีการเปรียบเทียบประสิทธิภาพที่เหมาะสมเนื่องจากประสิทธิภาพของวิธีการที่แตกต่างกันน่าจะขึ้นอยู่กับการกระจายข้อมูล 1) ระยะเวลานานเท่าไหร่ - ยิ่งช่วงเวลาสั้นลงเท่าไหร่ตารางปฏิทินที่ดีกว่าจะทำงานได้เนื่องจากช่วงเวลาที่ยาวนานจะสร้างแถวกลางจำนวนมาก 2) ความถี่ในการทับซ้อนกันบ่อยครั้งเท่าใด - ช่วงเวลาส่วนใหญ่ไม่ทับซ้อนกัน . ฉันคิดว่าประสิทธิภาพของโซลูชันของ Itzik ขึ้นอยู่กับว่า อาจมีวิธีอื่นในการเบ้ข้อมูลเหล่านี้เป็นเพียงส่วนน้อยที่นึกถึง
Vladimir Baranov

2

ฉันคิดว่านี่จะตรงไปตรงมากับตารางปฏิทินเช่นบางสิ่งเช่นนี้

SELECT i.CustID, COUNT( DISTINCT c.calendarDate ) days
FROM #Items i
    INNER JOIN calendar.main c ON c.calendarDate Between i.StartDate And i.EndDate
GROUP BY i.CustID

อุปกรณ์ทดสอบ

USE tempdb
GO

-- Cutdown calendar script
IF OBJECT_ID('dbo.calendar') IS NULL
BEGIN

    CREATE TABLE dbo.calendar (
        calendarId      INT IDENTITY(1,1) NOT NULL,
        calendarDate    DATE NOT NULL,

        CONSTRAINT PK_calendar__main PRIMARY KEY ( calendarDate ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
        CONSTRAINT UK_calendar__main UNIQUE NONCLUSTERED ( calendarId ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
END
GO


-- Populate calendar table once only
IF NOT EXISTS ( SELECT * FROM dbo.calendar )
BEGIN

    -- Populate calendar table
    WITH cte AS
    (
    SELECT 0 x
    UNION ALL
    SELECT x + 1
    FROM cte
    WHERE x < 11323 -- Do from year 1 Jan 2000 until 31 Dec 2030 (extend if required)
    )
    INSERT INTO dbo.calendar ( calendarDate )
    SELECT
        calendarDate
    FROM
        (
        SELECT 
            DATEADD( day, x, '1 Jan 2010' ) calendarDate,
            DATEADD( month, -7, DATEADD( day, x, '1 Jan 2010' ) ) academicDate
        FROM cte
        ) x
    WHERE calendarDate < '1 Jan 2031'
    OPTION ( MAXRECURSION 0 )

    ALTER INDEX ALL ON dbo.calendar REBUILD

END
GO





IF OBJECT_ID('tempdb..Items') IS NOT NULL DROP TABLE Items
GO

CREATE TABLE dbo.Items
    (
    CustID INT NOT NULL,
    ItemID INT NOT NULL,
    StartDate DATE NOT NULL,
    EndDate DATE NOT NULL,

    INDEX _cdx_Items CLUSTERED ( CustID, StartDate, EndDate )
    )
GO

INSERT INTO Items ( CustID, ItemID, StartDate, EndDate )
SELECT 11205, 20009, '2015-01-23',  '2015-01-26'  
UNION ALL 
SELECT 11205, 20010, '2015-01-24',  '2015-01-24'    
UNION ALL  
SELECT 11205, 20011, '2015-01-23',  '2015-01-26' 
UNION ALL  
SELECT 11205, 20012, '2015-01-23',  '2015-01-27'  
UNION ALL  
SELECT 11205, 20012, '2015-01-23',  '2015-01-27'   
UNION ALL  
SELECT 11205, 20012, '2015-01-28',  '2015-01-29'
GO


-- Scale up : )
;WITH cte AS (
SELECT TOP 1000000 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO Items ( CustID, ItemID, StartDate, EndDate )
SELECT 11206 + rn % 999, 20012 + rn, DATEADD( day, rn % 333, '1 Jan 2015' ), DATEADD( day, ( rn % 333 ) + rn % 7, '1 Jan 2015' )
FROM cte
GO
--:exit



-- My query: Pros: simple, one copy of items, easy to understand and maintain.  Scales well to 1 million + rows.
-- Cons: requires calendar table.  Others?
SELECT i.CustID, COUNT( DISTINCT c.calendarDate ) days
FROM dbo.Items i
    INNER JOIN dbo.calendar c ON c.calendarDate Between i.StartDate And i.EndDate
GROUP BY i.CustID
--ORDER BY i.CustID
GO


-- Vladimir query: Pros: Effectively same as above
-- Cons: I wouldn't use CROSS APPLY where it's not necessary.  Fortunately optimizer simplifies avoiding RBAR (I think).
-- Point of style maybe, but in terms of queries being self-documenting I prefer number 1.
SELECT T.CustID, COUNT( DISTINCT CA.calendarDate ) AS TotalCount
FROM
    Items AS T
    CROSS APPLY
    (
        SELECT c.calendarDate
        FROM dbo.calendar c
        WHERE
            c.calendarDate >= T.StartDate
            AND c.calendarDate <= T.EndDate
    ) AS CA
GROUP BY T.CustID
--ORDER BY T.CustID
--WHERE T.CustID = 11205
GO


/*  WARNING!! This is commented out as it can't compete in the scale test.  Will finish at scale 100, 1,000, 10,000, eventually.  I got 38 mins for 10,0000.  Pegs CPU.  

-- Julian:  Pros; does not require calendar table.
-- Cons: over-complicated (eg versus Query 1 in terms of number of lines of code, clauses etc); three copies of dbo.Items table (we have already shown
-- this query is possible with one); does not scale (even at 100,000 rows query ran for 38 minutes on my test rig versus sub-second for first two queries).  <<-- this is serious.
-- Indexing could help.
SELECT DISTINCT
    CustID,
     StartDate = CASE WHEN itmin.StartDate < its.StartDate THEN itmin.StartDate ELSE its.StartDate END
    , EndDate = CASE WHEN itmax.EndDate > its.EndDate THEN itmax.EndDate ELSE its.EndDate END
FROM Items its
OUTER APPLY (
    SELECT StartDate = MIN(StartDate) FROM Items std
    WHERE std.ItemID <> its.ItemID AND (
        (std.StartDate <= its.StartDate AND std.EndDate >= its.StartDate)
        OR (std.StartDate >= its.StartDate AND std.StartDate <= its.EndDate)
    )
) itmin
OUTER APPLY (
    SELECT EndDate = MAX(EndDate) FROM Items std
    WHERE std.ItemID <> its.ItemID AND (
        (std.EndDate >= its.StartDate AND std.EndDate <= its.EndDate)
        OR (std.StartDate <= its.EndDate AND std.EndDate >= its.EndDate)
    )
) itmax
GO
*/

-- ypercube:  Pros; does not require calendar table.
-- Cons: over-complicated (eg versus Query 1 in terms of number of lines of code, clauses etc); four copies of dbo.Items table (we have already shown
-- this query is possible with one); does not scale well; at 1,000,000 rows query ran for 2:20 minutes on my test rig versus sub-second for first two queries.
WITH 
  start_dates AS
    ( SELECT CustID, StartDate,
             Rn = ROW_NUMBER() OVER (PARTITION BY CustID 
                                     ORDER BY StartDate)
      FROM items AS i
      WHERE NOT EXISTS
            ( SELECT *
              FROM Items AS j
              WHERE j.CustID = i.CustID
                AND j.StartDate < i.StartDate AND i.StartDate <= j.EndDate 
            )
      GROUP BY CustID, StartDate
    ),
  end_dates AS
    ( SELECT CustID, EndDate,
             Rn = ROW_NUMBER() OVER (PARTITION BY CustID 
                                     ORDER BY EndDate) 
      FROM items AS i
      WHERE NOT EXISTS
            ( SELECT *
              FROM Items AS j
              WHERE j.CustID = i.CustID
                AND j.StartDate <= i.EndDate AND i.EndDate < j.EndDate 
            )
      GROUP BY CustID, EndDate
    )
SELECT s.CustID, 
       Result = SUM( DATEDIFF(day, s.StartDate, e.EndDate) + 1 )
FROM start_dates AS s
  JOIN end_dates AS e
    ON  s.CustID = e.CustID
    AND s.Rn = e.Rn 
GROUP BY s.CustID ;

2
แม้ว่ามันจะทำงานได้ดี แต่คุณควรอ่านนิสัยแย่ ๆ ที่จะเตะ: แบบสอบถามวันที่ / ช่วงที่ไม่ถูกต้อง : บทสรุปที่ 2 หลีกเลี่ยงระหว่างข้อความค้นหาช่วงจาก DATETIME, SMALLDATETIME, DATETIME2 และ DATETIMEOFFSET
Julien Vavasseur
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.