Группировка значений из таблицы по операции Min / Max / Avg - PullRequest
4 голосов
/ 05 апреля 2011

Вот мой сценарий:

У меня есть одна таблица с 2 столбцами.ID и значение.ID - это int, а значение - действительное.

ID    Value
1     6.7
2     8.9
3     4.5
5     3.2
8     2.5
9     2.1
10    1.0
15    2.3
18    2.4
19    4.0
20    3.2

Я хотел бы составить SP, который получает номер группировки (Group) и операцию (Op) и возвращает новую таблицу следующим образом:

Group = 2, Op = Max

IDstart   IDend    Value
1         2        8.9
3         5        4.5
8         9        2.5
10        15       2.3
18        19       4.0 
20        20       3.2

Group = 3, Op = Min

IDstart   IDend    Value
1         3        4.5
5         9        2.1
10        18       1.0
19        20       3.2

Группа определяет, сколько строк объединить в одну строку (вновая таблица), а операция определяет, какую операцию нужно выполнить с группой строк; мне нужны следующие операции: максимум, минимум и среднее.Последняя группа может содержать меньше строк, чем все остальные группы.если последняя группа имеет одно значение IDstart = IDEnd.Идентификатор уникален, но может иметь пробелы.

Я ищу самый быстрый способ сделать это, любая помощь будет оценена.

Использование SQL Server 2008 R2

Гилад.

Ответы [ 3 ]

1 голос
/ 05 апреля 2011

Аргументация выглядит следующим образом

  • Использование функции ROW_NUMBER() и некоторой арифметики позволяет создать фиктивный столбец, помещая каждый идентификатор в группу указанного вами размера.
  • результат этого оператора можно сгруппировать, а указанный оператор можно применить с помощью оператора CASE.Если вам нужны дополнительные операторы, вам нужно будет только развернуть этот оператор CASE.

Script

DECLARE @Group INTEGER
DECLARE @Op VARCHAR(3)

SET @Group = 3
SET @Op = 'MIN'

;WITH q(ID, Value) AS (
  SELECT 1,     6.7
  UNION ALL SELECT 2,     8.9
  UNION ALL SELECT 3,     4.5
  UNION ALL SELECT 5,     3.2
  UNION ALL SELECT 8,     2.5
  UNION ALL SELECT 9,     2.1
  UNION ALL SELECT 10,    1.0
  UNION ALL SELECT 15,    2.3
  UNION ALL SELECT 18,    2.4
  UNION ALL SELECT 19,    4.0
  UNION ALL SELECT 20,    3.2
)
SELECT  [IDStart] = MIN(ID)
        , [IDEnd] = MAX(ID)
        , [Value] = CASE  WHEN @Op = 'MAX' THEN MAX(Value)
                          WHEN @Op = 'MIN' THEN MIN(Value)
                          WHEN @Op = 'AVG' THEN AVG(Value)
                    END
FROM    (
          SELECT ID
                 , Value
                 , GroupRow = (ROW_NUMBER() OVER (ORDER BY ID) - 1) / @Group
          FROM    q
        ) q          
GROUP BY
        GroupRow
1 голос
/ 05 апреля 2011

Вы можете найти это полезным:

SET @idx = 0;
SET @grp_size = 3;
SELECT MIN(`temp1`.`id`) as `IDstart`, MAX(`temp1`.`id`) as `IDend`, AVG(`temp1`.`value`) as `agregate`
FROM (
    SELECT ID AS `id` , @idx := @idx +1 / @grp_size , FLOOR( @idx ) AS `grouper`, `value`
    FROM `test1`
) as `temp1`
GROUP BY `temp1`.`grouper`

Это для MySQL, но должно быть похоже на SQL Server.

0 голосов
/ 05 апреля 2011

Это соответствует вашим требованиям. Измените значение параметра @op на MIN, MAX или AVG, а параметр @Group на размер группы. Функция ранжирования NTILE используется для разделения групп, затем ROW_NUMBER для идентификации первого / последнего члена каждой группы.

DECLARE @t TABLE
(id INT,
 VALUE REAL
)

INSERT @t (id,VALUE)
VALUES 
(1,     6.7),
(2,     8.9),
(3,     4.5),
(5,     3.2),
(8,     2.5),
(9,     2.1),
(10,    1.0),
(15,    2.3),
(18,    2.4),
(19,    4.0),
(20,    3.2)

DECLARE @Group DECIMAL(5,1) = 3.0
DECLARE @Bucket INT
DECLARE @op char(3) = 'MIN' --MAX, AVG
SELECT @Bucket = CEILING(COUNT(1)/@Group)
FROM @t

;WITH bucketCTE
AS
(
    SELECT *,NTILE(@Bucket) OVER (ORDER BY id) bucket
    FROM @t
)
,rankCTE
AS
(
    SELECT *, ROW_NUMBER() OVER (PARTITION BY bucket
                                 ORDER BY id ASC
                                ) AS rn,
            ROW_NUMBER() OVER (PARTITION BY bucket
                                 ORDER BY id DESC
                                ) AS rn2
    FROM bucketCTE
)
,groupCTE
AS
(
    SELECT AVG(VALUE) average, MIN(VALUE) minimum, MAX(VALUE) maximum,  bucket
    FROM bucketCTE
    GROUP BY bucket

)
SELECT r1.id minId, r2.id maxId , CASE  WHEN @op = 'AVG' THEN g.average
                                        WHEN @op = 'MIN' THEN g.minimum
                                        WHEN @op = 'MAX' THEN g.maximum
                                        ELSE NULL
                                  END AS value
FROM rankCTE AS r1
JOIN rankCTE AS r2
ON   r2.bucket = r1.bucket
AND  r2.rn2 = 1
JOIN groupCTE AS g
ON   g.bucket = r1.bucket
WHERE r1.rn = 1
ORDER BY r1.bucket
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...