Đây là phiên bản của tôi. Tôi thực sự đã trình bày điều này như một sự tò mò, để thể hiện một cách suy nghĩ khác về vấn đề này. Hóa ra nó hữu ích hơn thế bởi vì nó hoạt động tốt hơn cả giải pháp "đảo nhóm" mát mẻ của Martin Smith. Mặc dù, một khi ông đã thoát khỏi một số chức năng cửa sổ tổng hợp quá đắt tiền và đã làm tổng hợp thực thay vào đó, truy vấn của ông bắt đầu đá mông.
Giải pháp 1: Chạy 3 tháng trở lên, được thực hiện bằng cách kiểm tra 1 tháng trước và sau và sử dụng kết hợp bán chống lại điều đó.
WITH Months AS (
SELECT DISTINCT
O.CustID,
Grp = DateDiff(Month, '20000101', O.OrderDate)
FROM
CustOrder O
), Anchors AS (
SELECT
M.CustID,
Ind = M.Grp + X.Offset
FROM
Months M
CROSS JOIN (
SELECT -1 UNION ALL SELECT 0 UNION ALL SELECT 1
) X (Offset)
GROUP BY
M.CustID,
M.Grp + X.Offset
HAVING
Count(*) = 3
)
SELECT
C.CustName,
[Year] = Year(OrderDate),
O.OrderDate
FROM
Cust C
INNER JOIN CustOrder O ON C.CustID = O.CustID
WHERE
EXISTS (
SELECT 1
FROM
Anchors A
WHERE
O.CustID = A.CustID
AND O.OrderDate >= DateAdd(Month, A.Ind, '19991201')
AND O.OrderDate < DateAdd(Month, A.Ind, '20000301')
)
ORDER BY
C.CustName,
OrderDate;
Giải pháp 2: Mẫu chính xác 3 tháng. Nếu nó là một 4 tháng hoặc lớn hơn chạy, các giá trị được loại trừ. Điều này được thực hiện bằng cách kiểm tra trước 2 tháng và hai tháng sau (về cơ bản là tìm kiếm mẫu N, Y, Y, Y, N).
WITH Months AS (
SELECT DISTINCT
O.CustID,
Grp = DateDiff(Month, '20000101', O.OrderDate)
FROM
CustOrder O
), Anchors AS (
SELECT
M.CustID,
Ind = M.Grp + X.Offset
FROM
Months M
CROSS JOIN (
SELECT -2 UNION ALL SELECT -1 UNION ALL SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2
) X (Offset)
GROUP BY
M.CustID,
M.Grp + X.Offset
HAVING
Count(*) = 3
AND Min(X.Offset) = -1
AND Max(X.Offset) = 1
)
SELECT
C.CustName,
[Year] = Year(OrderDate),
O.OrderDate
FROM
Cust C
INNER JOIN CustOrder O ON C.CustID = O.CustID
INNER JOIN Anchors A
ON O.CustID = A.CustID
AND O.OrderDate >= DateAdd(Month, A.Ind, '19991201')
AND O.OrderDate < DateAdd(Month, A.Ind, '20000301')
ORDER BY
C.CustName,
OrderDate;
Dưới đây là kịch bản bảng nạp của tôi nếu ai muốn chơi:
IF Object_ID('CustOrder', 'U') IS NOT NULL DROP TABLE CustOrder
IF Object_ID('Cust', 'U') IS NOT NULL DROP TABLE Cust
GO
SET NOCOUNT ON
CREATE TABLE Cust (
CustID int identity(1,1) NOT NULL PRIMARY KEY CLUSTERED,
CustName varchar(100) UNIQUE
)
CREATE TABLE CustOrder (
OrderID int identity(100, 1) NOT NULL PRIMARY KEY CLUSTERED,
CustID int NOT NULL FOREIGN KEY REFERENCES Cust (CustID),
OrderDate smalldatetime NOT NULL
)
DECLARE @i int
SET @i = 1000
WHILE @i > 0 BEGIN
WITH N AS (
SELECT
Nm =
Char(Abs(Checksum(NewID())) % 26 + 65)
+ Char(Abs(Checksum(NewID())) % 26 + 97)
+ Char(Abs(Checksum(NewID())) % 26 + 97)
+ Char(Abs(Checksum(NewID())) % 26 + 97)
+ Char(Abs(Checksum(NewID())) % 26 + 97)
+ Char(Abs(Checksum(NewID())) % 26 + 97)
)
INSERT Cust
SELECT N.Nm
FROM N
WHERE NOT EXISTS (
SELECT 1
FROM Cust C
WHERE
N.Nm = C.CustName
)
SET @i = @i - @@RowCount
END
WHILE @i < 50000 BEGIN
INSERT CustOrder
SELECT TOP (50000 - @i)
Abs(Checksum(NewID())) % 1000 + 1,
DateAdd(Day, Abs(Checksum(NewID())) % 10000, '19900101')
FROM master.dbo.spt_values
SET @i = @i + @@RowCount
END
Performance
Dưới đây là một số kết quả thử nghiệm hiệu suất cho 3 tháng hoặc-nhiều truy vấn :
Query CPU Reads Duration
Martin 1 2297 299412 2348
Martin 2 625 285 809
Denis 3641 401 3855
Erik 1855 94727 2077
Đây chỉ là một lần chạy của mỗi số, nhưng các con số này khá đại diện. Nó chỉ ra rằng truy vấn của bạn không được thực hiện tồi tệ như vậy, Denis, sau khi tất cả. Truy vấn của Martin đánh bại những người khác xuống, nhưng lúc đầu đã sử dụng một số chiến lược chức năng cửa sổ quá đắt tiền mà anh ta đã sửa. Tất nhiên, như tôi đã lưu ý, truy vấn của Denis không kéo các hàng phải khi khách hàng có hai đơn đặt hàng trong cùng một ngày, vì vậy truy vấn của anh ta không có tranh chấp trừ khi anh ta cố định.
Ngoài ra, các chỉ mục khác nhau có thể làm rung chuyển mọi thứ. Tôi không biết.
Bạn muốn thêm đầu ra nào nếu hàng '113, 13-AUG-2007, 1' được thêm vào bảng Đặt hàng? Một khối đầu ra cho AA với 4 hàng, hoặc hai khối đầu ra, mỗi khối chứa 3 hàng? Nếu bạn thích, đó là "đúng ba tháng tại một thời điểm" hoặc "ba hoặc nhiều tháng tại một thời điểm". –
Xin lỗi vì sự chậm trễ, tôi thích chính xác ba tháng – Gopi
Bạn có nghĩa là một chuỗi 4 tháng sẽ trả về 6 hàng, một tập hợp với tháng 1, 2, 3 và một tập hợp khác với tháng 2, 3, 4 hoặc đơn giản để loại trừ tất cả các chuỗi đơn đặt hàng không chính xác 3 tháng? – ErikE