Tôi không thể truy vấn @Alex W để hoạt động. Nó không phải là tiêu chuẩn SQL, vì vậy nó yêu cầu rất nhiều viết lại để tương thích với SQL Server (mà tôi có thể kiểm tra). Nhưng nó đã cho tôi một số cảm hứng, mà tôi đã mở rộng.
Tìm tất cả khởi điểm của mỗi giai đoạn không bị gián đoạn chờ đợi:
SELECT DISTINCT
t1.ID,
t1.d1 AS date,
-DATEDIFF(DAY, (SELECT MIN(d1) FROM Orders), t1.d1) AS n
FROM Orders t1
LEFT JOIN Orders t2 -- Join for any events occurring while this
ON t2.ID = t1.ID -- is starting. If this is a start point,
AND t2.d1 <> t1.d1 -- it won't match anything, which is what
AND t1.d1 BETWEEN t2.d1 AND t2.d2 -- we want.
GROUP BY t1.ID, t1.d1, t1.d2
HAVING COUNT(t2.ID) = 0
Và tương đương cho end-điểm:
SELECT DISTINCT
t1.ID,
t1.d2 AS date,
DATEDIFF(DAY, (SELECT MIN(d1) FROM Orders), t1.d2) AS n
FROM Orders t1
LEFT JOIN Orders t2
ON t2.ID = t1.ID
AND t2.d2 <> t1.d2
AND t1.d2 BETWEEN t2.d1 AND t2.d2
GROUP BY t1.ID, t1.d1, t1.d2
HAVING COUNT(t2.ID) = 0
n
là số ngày kể từ một số chung điểm trong thời gian. Điểm bắt đầu có giá trị âm và điểm cuối có giá trị dương. Điều này là để chúng tôi có thể thêm chúng lên để có được số ngày ở giữa.
span = end - start
span = end + (-start)
span1 + span2 = end1 + (-start1) + end2 + (-start2)
Cuối cùng, chúng ta chỉ cần thêm những thứ lên:
SELECT ID, SUM(n) AS hold_days
FROM (
SELECT DISTINCT
t1.id,
t1.d1 AS date,
-DATEDIFF(DAY, (SELECT MIN(d1) FROM Orders), t1.d1) AS n
FROM Orders t1
LEFT JOIN Orders t2
ON t2.ID = t1.ID
AND t2.d1 <> t1.d1
AND t1.d1 BETWEEN t2.d1 AND t2.d2
GROUP BY t1.ID, t1.d1, t1.d2
HAVING COUNT(t2.ID) = 0
UNION ALL
SELECT DISTINCT
t1.id,
t1.d2 AS date,
DATEDIFF(DAY, (SELECT MIN(d1) FROM Orders), t1.d2) AS n
FROM Orders t1
LEFT JOIN Orders t2
ON t2.ID = t1.ID
AND t2.d2 <> t1.d2
AND t1.d2 BETWEEN t2.d1 AND t2.d2
GROUP BY t1.ID, t1.d1, t1.d2
HAVING COUNT(t2.ID) = 0
ORDER BY ID, date
) s
GROUP BY ID;
Input bảng (Orders):
ID d1 d2
1 2011-08-01 2011-08-08
1 2011-08-02 2011-08-06
1 2011-08-03 2011-08-10
1 2011-08-12 2011-08-14
2 2011-08-01 2011-08-03
2 2011-08-02 2011-08-06
2 2011-08-05 2011-08-09
Output:
ID hold_days
1 11
2 8
Ngoài ra, bạn có thể thực hiện việc này bằng quy trình được lưu trữ.
CREATE PROCEDURE CalculateHoldTimes
@ID int = 0
AS
BEGIN
DECLARE Events CURSOR FOR
SELECT *
FROM (
SELECT d1 AS date, 1 AS diff
FROM Orders
WHERE ID = @ID
UNION ALL
SELECT d2 AS date, -1 AS diff
FROM Orders
WHERE ID = @ID
) s
ORDER BY date;
DECLARE @Events_date date,
@Events_diff int,
@Period_start date,
@Period_accum int,
@Total_start date,
@Total_count int;
OPEN Events;
FETCH NEXT FROM Events
INTO @Events_date, @Events_diff;
SET @Period_start = @Events_date;
SET @Period_accum = 0;
SET @Total_start = @Events_date;
SET @Total_count = 0;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Period_accum = @Period_accum + @Events_diff;
IF @Period_accum = 1 AND @Events_diff = 1
-- Start of period
SET @Period_start = @Events_date;
ELSE IF @Period_accum = 0 AND @Events_diff = -1
-- End of period
SET @Total_count = @Total_count +
DATEDIFF(day, @Period_start, @Events_date);
FETCH NEXT FROM Events
INTO @Events_date, @Events_diff;
END;
SELECT
@Total_start AS d1,
@Events_date AS d2,
@Total_count AS hold_time;
END;
Gọi nó với:
tuyên bố
EXEC CalculateHoldTimes 1;
Tôi có một sự tham gia trái trên bảng lịch phụ trợ có thể hữu ích. –
có thể trùng lặp của [Cách tốt nhất để tìm khoảng trống trong một tập hợp các ngày tháng là gì?] (Http://stackoverflow.com/questions/4765495/what-is-a-good-way-to-find-gaps-in -a-set-of-datepans) –
@Brian, câu hỏi này rất khác nhau. OP, bạn có thể thêm (các) chế độ xem để hỗ trợ truy vấn không? –