İşte benim sürümüdür. Bunu gerçekten bir merak olarak sunarken, problem hakkında başka bir düşünme şekli gösterdim. Bundan daha kullanışlı olduğu ortaya çıktı çünkü Martin Smith'in serin "gruplanmış adalar" çözümünden bile daha iyi performans gösterdi. Yine de, aşırı pahalı agregate pencereleme fonksiyonlarından kurtulduktan ve gerçek agregatları yerine getirdikten sonra, sorgulaması popo atmaya başladı.
Çözüm 1: 3 ay veya daha uzun çalıştırır, 1 ay öncesinde ve arkasında check-in yapıp buna karşı yarı katılmak kullanılarak yapılır.
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;
Çözüm 2: Tam 3 aylık kalıpları. 4 aylık veya daha büyük bir çalışma ise değerler hariç tutulur. Bu, 2 ay önce ve iki ay sonra kontrol edilerek yapılır (esas olarak N, Y, Y, Y, N modellerini arar). Başkasının oynamak istiyorsa Burada
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;
benim masa yükleme komut dosyası:
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
Performans
İşte
3 aylık ya-fazla sorgu için bazı performans testleri sonuçları :
Query CPU Reads Duration
Martin 1 2297 299412 2348
Martin 2 625 285 809
Denis 3641 401 3855
Erik 1855 94727 2077
Bu tek bölgedir her biri, ancak sayılar oldukça temsilcisidir. Sorgunun bu kadar kötü performans göstermediği ortaya çıkıyor, Denis, sonuçta. Martin'in sorgusu diğerlerinin ellerini indirgemesine karşın, ilk başta sabitlediği aşırı pahalı pencereleme fonksiyonlarını kullanıyordu. Ben belirtildiği gibi onun sorgu o sabit olmadıkça yarışının dışına yani bir müşteri, aynı gün iki sipariş olduğunda Tabii
, Denis sorgu doğru satırların çekerek değildir. Ayrıca, farklı indeksler şeyleri sallayabilirdi. Bilmiyorum.
satır '113, 13-Ağustos-2007, 1' Sipariş tabloya eklenirse Ne istiyorsun çıktı? Her biri 3 satır içeren 4 satırlı veya iki blok çıkışlı AA için bir çıkış bloğu mu? İsterseniz, 'bir defada sadece üç ay' veya 'bir seferde üç veya daha fazla ay' demektir. –
Gecikme için özür dilerim, tam olarak üç ay tercih ederim – Gopi
4 aylık bir dize 6 satır, 1, 2, 3 ve 1, 2, 3, 4 ayları olan bir setin geri dönmesi anlamına mı geliyor? Tam olarak 3 ay olmayan tüm sipariş dizeleri? – ErikE