Группа SQL по модулю количества строк - PullRequest
4 голосов
/ 01 апреля 2010

У меня есть следующие образцы данных:

Id     Name     Quantity
1      Red      1
2      Red      3
3      Blue     1
4      Red      1
5      Yellow   3

Таким образом, для этого примера есть всего 5 красных, 1 синий и 3 желтых. Я ищу способ сгруппировать их по цвету, но с максимум 2 предметами на группу (сортировка не важна). Вот так:

Name     QuantityInPackage
Red      2
Red      2
Red      1
Blue     1
Yellow   2
Yellow   1

Любые предложения о том, как сделать это с помощью T-SQL на MS-SQL 2005?

Ответы [ 4 ]

6 голосов
/ 01 апреля 2010

Я бы определил таблицу, содержащую последовательные числа, скажем, от 1 до 1000 и присоединился бы к этой таблице (если ваша база данных не поддерживает генерацию этих чисел в запросе, как Oracle с использованием CONNECT BY):

Таблица №

n
1
2
3
...

Я пробовал следующий запрос с использованием Oracle (должен работать и с TSQL):

With summed_colors As (
  Select name, Sum(quantity) quantity
  From colors
  Group By name
)
Select
  name,
  Case When n*2-1 = quantity Then 1 Else 2 End quantityInPackage
From summed_colors
Join nums On ( n*2-1 <= quantity )
Order By name, quantityInPackage Desc

, и он возвращает

Blue   1
Red    2
Red    2
Red    1
Yellow 2
Yellow 1
1 голос
/ 01 апреля 2010

Вам нужно использовать таблицу чисел, чтобы разворачивать данные, чтобы сделать несколько строк:

DECLARE @PackageSize AS int
SET @PackageSize = 2

DECLARE @numbers AS TABLE (Number int)
INSERT  INTO @numbers
VALUES  (1)
INSERT  INTO @numbers
VALUES  (2)
INSERT  INTO @numbers
VALUES  (3)
INSERT  INTO @numbers
VALUES  (4)
INSERT  INTO @numbers
VALUES  (5)
INSERT  INTO @numbers
VALUES  (6)
INSERT  INTO @numbers
VALUES  (7)
INSERT  INTO @numbers
VALUES  (8)
INSERT  INTO @numbers
VALUES  (9)
INSERT  INTO @numbers
VALUES  (10)

DECLARE @t AS TABLE
    (
     Id int
    ,Nm varchar(6)
    ,Qty int
    )
INSERT  INTO @t
VALUES  (1, 'Red', 1)
INSERT  INTO @t
VALUES  (2, 'Red', 3)
INSERT  INTO @t
VALUES  (3, 'Blue', 1)
INSERT  INTO @t
VALUES  (4, 'Red', 1)
INSERT  INTO @t
VALUES  (5, 'Yellow', 3) ;
WITH    Totals
          AS (
              SELECT    Nm
                       ,SUM(Qty) AS TotalQty
                       ,SUM(Qty) / @PackageSize AS NumCompletePackages
                       ,SUM(Qty) % @PackageSize AS PartialPackage
              FROM      @t
              GROUP BY  Nm
             )
    SELECT  Totals.Nm
           ,@PackageSize AS QuantityInPackage
    FROM    Totals
    INNER JOIN @numbers AS numbers
            ON numbers.Number <= Totals.NumCompletePackages
    UNION ALL            
    SELECT  Totals.Nm
           ,PartialPackage AS QuantityInPackage
    FROM    Totals
    WHERE PartialPackage <> 0
0 голосов
/ 01 апреля 2010

Это очень грубо, но работает.

CREATE TABLE #Colors
    (
    Id int,
    Name varchar(50),
    Quantity int
    )

INSERT INTO #Colors VALUES (1, 'Red', 1)
INSERT INTO #Colors VALUES (2, 'Red', 3)
INSERT INTO #Colors VALUES (3, 'Blue', 1)
INSERT INTO #Colors VALUES (4, 'Red', 1)
INSERT INTO #Colors VALUES (5, 'Yellow', 3)
INSERT INTO #Colors VALUES (6, 'Green', 2)

SELECT
    Name,
    SUM(Quantity) AS TotalQuantity
INTO #Summed
FROM
    #Colors
GROUP BY
    Name

SELECT
    Name,
    TotalQuantity / 2 AS RecordsWithQuantity2,
    TotalQuantity % 2 AS RecordsWithQuantity1
INTO #SortOfPivot
FROM
    #Summed
ORDER BY
    Name

DECLARE @RowCount int
SET @RowCount = (SELECT COUNT(*) FROM #SortOfPivot)
DECLARE @Name varchar(50)
DECLARE @TwosInsertCount int
DECLARE @OnesInsertCount int

CREATE TABLE #Result (Name varchar(50), Quantity int)

WHILE @RowCount > 0
BEGIN
    SET @Name = (SELECT TOP 1 Name FROM #SortOfPivot)
    SET @TwosInsertCount = (SELECT TOP 1 RecordsWithQuantity2 FROM #SortOfPivot)
    SET @OnesInsertCount = (SELECT TOP 1 RecordsWithQuantity1 FROM #SortOfPivot)
    WHILE @TwosInsertCount > 0
    BEGIN
        INSERT INTO #Result (Name, Quantity) VALUES (@Name, 2)
        SET @TwosInsertCount = @TwosInsertCount - 1
    END
    WHILE @OnesInsertCount > 0
    BEGIN
        INSERT INTO #Result (Name, Quantity) VALUES (@Name, 1)
        SET @OnesInsertCount =  @OnesInsertCount - 1
    END
    DELETE FROM #SortOfPivot WHERE Name = @Name
    SET @RowCount = (SELECT COUNT(*) FROM #SortOfPivot)
END

SELECT * FROM #Result
DROP TABLE #Colors
DROP TABLE #Result
DROP TABLE #Summed
DROP TABLE #SortOfPivot
0 голосов
/ 01 апреля 2010

Здесь важна не группировка или модуль / деление, а тот факт, что вам нужно выполнить агрегирование (сумму) и затем снова проанализировать данные. На самом деле строк "Red 2" не существует, их нужно каким-то образом создавать.

Для SQL Server 2005+ я бы, вероятно, использовал функцию "взрыва":

CREATE FUNCTION dbo.CreateBuckets
(
    @Num int,
    @MaxPerGroup int
)
RETURNS TABLE
AS RETURN
WITH First_CTE AS
(
    SELECT CASE
        WHEN @MaxPerGroup < @Num THEN @MaxPerGroup
        ELSE @Num
    END AS Seed
),
Sequence_CTE AS
(
    SELECT Seed AS [Current], Seed AS Total
    FROM First_CTE

    UNION ALL

    SELECT
        CASE
            WHEN (Total + @MaxPerGroup) > @Num THEN (@Num - Total)
            ELSE @MaxPerGroup
        END,
        Total + @MaxPerGroup
    FROM Sequence_CTE
    WHERE Total < @Num
)
SELECT [Current] AS Num
FROM Sequence_CTE

Затем в основном запросе сначала группируйте (суммируйте) данные, а затем используйте функцию сегмента:

WITH Totals AS
(
    SELECT Name, SUM(Quantity) AS Total
    FROM Table
    GROUP BY Name
)
SELECT Name, b.Num AS QuantityInPackage
FROM Totals
CROSS APPLY dbo.CreateBuckets(Total, 2) b

Это должно работать для любого размера сегмента, не должно быть 2 (просто измените параметр).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...