2010-04-01 8 views
4

je les données exemple suivant:groupe SQL par Modulo de rang comte

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

Donc, pour cet exemple, il y a un total de 5 rouge, 1 bleu et jaune 3. Je cherche un moyen de les regrouper par couleur, mais avec un maximum de 2 items par groupe (le tri n'est pas important). Comme si:

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

Des suggestions sur la façon d'accomplir cela en utilisant T-SQL sur MS-SQL 2005?

+0

Quelle version de SQL Server ou Informix? NTILE n'est pas tout à fait ce que vous voulez ... –

+0

Comment obtenez-vous du premier ensemble au deuxième ensemble? Si vous 'GROUP BY', alors vous allez avoir seulement une ligne par couleur. –

+0

Raj Plus: Exactement ma question. La quantité dans le paquet est un maximum de 2. Ainsi, il peut être 1 ou 2. Si un "Couleur" a plus de 2 éléments, alors il doit être affiché sur la ligne suivante. –

Répondre

6

Je définirais un tableau contenant des numéros séquentiels, disent 1-1000 et rejoindre cette table (à moins que votre base de données prend en charge la génération de ces numéros dans la requête comme Oracle à l'aide CONNECT BY):

Tableau num

n 
1 
2 
3 
... 

J'ai essayé la requête suivante en utilisant Oracle (devrait fonctionner avec TSQL aussi):

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 

et il retourne

Blue 1 
Red 2 
Red 2 
Red 1 
Yellow 2 
Yellow 1 
+0

+1 Bonne réponse! – CResults

+0

Très bien. Merci! –

1

Vous devez utiliser une table de numéros pour UNPIVOT vos données, plusieurs lignes:

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

Ce n'est pas groupement ou modulo/division qui est la partie difficile ici, il est le fait que vous devez faire un agrégat (somme) et ensuite exploser les données à nouveau. Il n'y a pas vraiment de lignes "Red 2", vous devez les créer d'une manière ou d'une autre.

Pour SQL Server 2005+, je serais probablement utiliser une fonction ne la « explosion »:

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 

Puis, dans la requête principale, groupe (somme) les données, puis utilisez la fonction seau:

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 

Cela devrait fonctionner pour n'importe quelle taille de baquet, ne doit pas être 2 (juste changer le paramètre).

0

Ceci est très brut, mais cela fonctionne.

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