SQL: группировать по последовательным записям
немного сложный вопрос SQL (мы запускаем SQL server 2000).
у меня есть следующая таблица, StoreCount -
WeekEndDate StoreCount
2010-07-25 359
2010-07-18 359
2010-07-11 358
2010-07-04 358
2010-06-27 358
2010-06-20 358
2010-06-13 358
2010-06-06 359
2010-05-30 360
2010-05-23 360
2010-05-16 360
Я хочу превратить это в следующий вывод -
StartDate EndDate StoreCount
2010-07-18 2010-07-25 359
2010-06-13 2010-07-11 358
2010-06-06 2010-06-06 359
2010-05-16 2010-05-30 360
Как вы можете видеть, я хочу сгруппировать подсчеты магазина, только когда они работают в последовательности вместе.
5 ответов
вот удар по банке, только он может иметь синтаксис, недоступный в SS2k. На самом деле это было написано на Oracle, так как у меня больше нет этой версии SS. Единственным подвохом может быть выбор выбора...(прошло некоторое время с тех пор, как я использовал SS2k, поэтому трудно вспомнить, какие функции не были доступны тогда.)
select min(weekenddate) as start_date, end_date, storecount
from (
select s1.weekenddate
, (select max(weekenddate)
from store_stats s2
where s2.storecount = s1.storecount
and not exists (select null
from store_stats s3
where s3.weekenddate < s2.weekenddate
and s3.weekenddate > s1.weekenddate
and s3.storecount <> s1.storecount)
) as end_date
, s1.storecount
from store_stats s1
) result
group by end_date, storecount
order by 1 desc
START_DATE END_DATE STORECOUNT
---------- ---------- ----------
2010-07-18 2010-07-25 359
2010-06-13 2010-07-11 358
2010-06-06 2010-06-06 359
2010-05-16 2010-05-30 360
используйте курсор. Я не знаю, как это сделать в sql2k с помощью запроса.
DECLARE @w datetime
DECLARE @s int
DECLARE @prev_s int
DECLARE @start_w datetime
DECLARE @end_w datetime
CREATE TABLE #zz(start datetime, [end] datetime, StoreCount int)
DECLARE a_cursor CURSOR
FOR SELECT WeekEndDate, StoreCount FROM Line ORDER BY WeekEndDate DESC, StoreCount
OPEN a_cursor
FETCH NEXT FROM a_cursor INTO @w, @s
WHILE @@FETCH_STATUS = 0
BEGIN
IF @end_w IS NULL
BEGIN
SET @end_w = @w
SET @start_w = @w
SET @prev_s = @s
END
ELSE IF @prev_s <> @s
BEGIN
INSERT INTO #zz values(@start_w, @end_w, @prev_s)
SET @end_w = @w
SET @start_w = @w
SET @prev_s = @s
END ELSE
SET @start_w = @w
FETCH NEXT FROM a_cursor INTO @w, @s
END
-- add last one
INSERT INTO #zz values(@start_w, @end_w, @prev_s)
CLOSE a_cursor
DEALLOCATE a_cursor
SELECT * FROM #zz ORDER BY 1 DESC
DROP TABLE #zz
хорошо, вот мой идти на это.
DECLARE @curDate DATETIME = (SELECT MIN(WeekEndDate) FROM Table_1);
DECLARE @curCount INT = (SELECT StoreCount FROM Table_1 WHERE WeekEndDate = @curDate);
DECLARE @sDate DATETIME = GETDATE()
DECLARE @eDate DATETIME = 0
WHILE @eDate < (SELECT MAX(WeekEndDate) FROM Table_1)
BEGIN
SELECT @sDate = (SELECT WeekEndDate AS StartDate FROM Table_1 WHERE WeekEndDate = @curDate) -- SELECT START DATE
-- NOW GET THE END DATE IN THIS GROUP
DECLARE @d1 DATETIME = @curDate
DECLARE @d2 DATETIME = @curDate
DECLARE @loop INT = 1
WHILE @loop = 1
BEGIN
IF ((SELECT StoreCount FROM Table_1 WHERE WeekEndDate = @d1) <> @curCount OR @d1 = (SELECT MAX(WeekEndDate) FROM Table_1)) BEGIN
SELECT @eDate = (SELECT TOP(1) WeekEndDate FROM Table_1 WHERE StoreCount = @curCount AND WeekEndDate = @d2 ORDER BY WeekEndDate DESC)
SELECT @loop = 0 END
ELSE BEGIN
SELECT @d2 = @d1
SELECT @d1 = (SELECT TOP(1) WeekEndDate FROM Table_1 WHERE WeekEndDate > @d1 ORDER BY WeekEndDate) END
END
SELECT @sDate AS StartDate, @eDate AS EndDate, @curCount AS StoreCount -- DO QHATEVER YOU NEED TO DO WITH THE RECORDS HERE
SELECT TOP(1) @curDate = WeekEndDate, @curCount = StoreCount
FROM Table_1
WHERE WeekEndDate > @eDate
GROUP BY WeekEndDate, StoreCount
ORDER BY WeekEndDate ASC
END
Я не уверен, как это объяснить, но, похоже, это дает желаемый результат для небольшого набора данных. По сути, он определяет точки в ряду, где значения изменяются.
Я не смотрел на план запроса, может быть болезненным.
пробовал на сервере Sybase, поэтому синтаксис должен быть совместим с SQL Server 2K.
SELECT x.StartDate
, MIN( y.EndDate ) AS EndDate
, x.StoreCount
FROM
( SELECT
wed1.WeekEndDate AS StartDate
, wed1.StoreCount
FROM
wed wed1
LEFT JOIN
wed wed2
ON wed1.WeekEndDate = DATEADD( DAY, 7, wed2.WeekEndDate )
WHERE
wed1.StoreCount != ISNULL( wed2.StoreCount, wed1.StoreCount - 1 )
) x,
( SELECT
wed1.WeekEndDate AS EndDate
FROM
wed wed1
LEFT JOIN
wed wed2
ON wed1.WeekEndDate = DATEADD( DAY, -7, wed2.WeekEndDate )
WHERE
wed1.StoreCount != ISNULL( wed2.StoreCount, wed1.StoreCount - 1 )
) y
WHERE
y.EndDate >= x.StartDate
GROUP BY
x.StartDate
HAVING
x.StartDate = MIN( x.StartDate )
ORDER BY
1 DESC
StartDate EndDate StoreCount
------------ ------------ -----------
Jul 18 2010 Jul 25 2010 359
Jun 13 2010 Jul 11 2010 358
Jun 6 2010 Jun 6 2010 359
May 16 2010 May 30 2010 360
попробуйте это простое решение:
create table x (weekEndDate char(10), storeCount int);
insert into x values
('2010-07-25',359),
('2010-07-18',359),
('2010-07-11',358),
('2010-07-04',358),
('2010-06-27',358),
('2010-06-20',358),
('2010-06-13',358),
('2010-06-06',359),
('2010-05-30',360),
('2010-05-23',360),
('2010-05-16',360);
select min(weekenddate) as startdate, max(weekenddate) as enddate, min(storecount) as storecount
from
(select weekenddate, storecount, concat(row_number() over (order by weekenddate) -row_number() over (partition by storecount order by weekenddate),'|',storecount) as groupkey from x) w
group by groupkey order by startdate desc;