2010-06-12 6 views
5

J'ai une table avec:objets SQL Count unique défini par des paramètres

id | parameter 
1 | A 
1 | B 
2 | A 
3 | A 
3 | B 

qui représentent des objets définis avec les valeurs:

1 -> A,B 
2 -> A 
3 -> A,B 

Je veux compter le nombre d'objets avec des paramètres différents en utilisant une requête SQL, donc dans ce cas il y aurait 2 objets uniques comme 1 et 3 ont les mêmes paramètres.

Il n'y a pas de contrainte sur le nombre de paramètres, il peut y avoir 0 ou tout autre nombre.

La base de données est un serveur Microsoft SQL Server 2000. Mais cela ne me dérange pas de connaître la solution pour d'autres bases de données.

+0

Comment représenter zéro paramètres? Une valeur NULL dans la colonne 'parameters' et une contrainte ou un trigger pour empêcher tout non-NULL pour ce même' id'? – pilcrow

+0

@pilcrow: Bien sûr, il existe une autre table avec l'ID d'objet en tant que clé primaire. – Eduardo

Répondre

0

Vous pouvez utiliser une having clause pour filtrer deux paramètres uniques:

select count(*) 
from YourTable 
group by 
     id 
having count(distinct parameter) > 1 
+0

J'ai modifié la recherche pour clarifier que: Il n'y a pas de contrainte sur le nombre de paramètres pouvant être 0, ou n'importe quel autre nombre. – Eduardo

3

Si je comprends bien, vous voulez que le nombre de distinctes combinaisons de parameter s par id représenté dans votre table, éventuellement avec le nombre d'entités présentant chacune de ces combinaisons distinctes.

Je ne peux pas parler pour SQL Server, mais sous MySQL vous pourriez faire quelque chose comme ceci:

SELECT parameter_set, COUNT(*) AS entity_count 
    FROM (
      -- Here we "flatten" the different parameter combinations per id 
      SELECT id, 
        GROUP_CONCAT(parameter ORDER BY parameter) AS parameter_set 
       FROM tbl 
      GROUP BY id 
     ) d 
GROUP BY parameter_set; 

qui vous donnera ceci:

parameter_set | entity_count 
---------------+-------------- 
A,B   |   2 -- two entities have params A, B 
A    |   1 -- one entity has param A 

et SELECT COUNT(DISTINCT parameter_set FROM (... flattening query ...)) d vous donnera le numéro d'ensembles de paramètres distincts.

+0

Oui, cela fonctionnerait pour MySQL mais j'ai besoin de la solution pour Microsoft SQL Server 2000 – Eduardo

+0

Si possible, je suggère de mettre en œuvre votre propre fonction de concaténation de groupe (http://sqlblog.com/blogs/adam_machanic/archive/2006 /07/12/rowset-string-concatenation-which-method-is-best.aspx, par exemple, bien que la recherche de "concaténation de lignes" en cède d'autres). Je pense que cette solution est simple et intuitive. –

+0

Cela peut ne pas fonctionner comme le souhaite l'OP. Il dit qu'un ID peut avoir zéro paramètre - ce qui signifie généralement une valeur nulle soit directement dans la table ou dans une jointure à gauche. Mais 'GROUP_CONCAT' ignore les valeurs NULL. Cela devrait donner un résultat invalide. (Je dis "should" verus "will" seulement parce que je n'ai pas de serveur MySQL à portée de main pour le moment). –

2

D'accord, voici ma tentative. Il pourrait être possible d'implémenter cette logique d'une manière qui ne nécessite pas 5 accès à la même table, mais je ne peux pas y penser maintenant.

La logique ici est d'éliminer d'abord les objets en double, puis de compter les ID restants. La sous-requête NOT IN représente les objets qui ont un objet correspondant avec un ID plus petit. La sous-requête joint les paramètres de deux objets t1 et t2, puis compte le nombre de paramètres correspondant à chaque paire t1/t2. Si le nombre de paramètres correspondants est le même que le nombre de paramètres dans t1 et dans t2, alors t2 et t1 sont des correspondances et nous devons exclure t1 du jeu de résultats.

DECLARE @tab TABLE (ID int, parameter varchar(2)); 

INSERT INTO @tab 
SELECT 1, 'A' UNION ALL 
SELECT 1, 'B' UNION ALL 
SELECT 2, 'A' UNION ALL 
SELECT 3, 'A' UNION ALL 
SELECT 3, 'B' UNION ALL 
SELECT 4, 'A' UNION ALL 
SELECT 5, 'C' UNION ALL 
SELECT 5, 'D'; 

SELECT 
    COUNT(DISTINCT t.ID) AS num_groups 
FROM 
    @tab AS t 
WHERE 
    t.ID NOT IN 
     (SELECT 
      t1.ID AS ID1 
     FROM 
       @tab AS t1 
      INNER JOIN 
       @tab AS t2 
      ON 
       t1.ID > t2.ID AND 
       t1.parameter = t2.parameter 
     GROUP BY 
      t1.ID, 
      t2.ID 
     HAVING 
      COUNT(*) = (SELECT COUNT(*) FROM @tab AS dupe WHERE dupe.ID = t1.ID) AND 
      COUNT(*) = (SELECT COUNT(*) FROM @tab AS dupe WHERE dupe.ID = t2.ID) 
     ); 

Résultat sur SQL Server 2008 R2:

num_groups 
3 

En ce qui concerne les objets avec 0 paramètres, cela dépend de la façon dont ils sont stockés, mais en général, vous auriez juste besoin d'ajouter un à la répondez ci-dessus s'il y a des objets avec 0 paramètres.

+0

En fait, cela ne fonctionne pas. Il donnera des valeurs incorrectes si plus d'un ID a zéro paramètre. –

1

Il n'existe pas de méthode infaillible pour ce faire dans SQL Server 2000, avec les conditions spécifiées, mais ce qui suit fonctionnera dans la plupart des situations et il vous avertira si cela ne fonctionne pas.

table donnée, "TBL":

ID Parameter 
1  A 
1  B 
2  A 
3  A 
3  B 
4  A 
4  NULL 
5  C 
5  D 
6  NULL 

.
Créer cette fonction:

CREATE FUNCTION MakeParameterListFor_tblID (@ID INT) 
RETURNS VARCHAR(8000) 
AS 
BEGIN 
    DECLARE 
     @ParameterList VARCHAR(8000), 
     @ListLen  INT 
    SET 
     @ParameterList = '' 

    SELECT 
     @ParameterList = @ParameterList + COALESCE (Parameter, '*null*') + ', ' 
    FROM 
     tbl 
    WHERE 
     ID = @ID 
    ORDER BY 
     Parameter 


    SET @ListLen  = LEN (@ParameterList) 
    IF @ListLen > 7800 -- 7800 is a SWAG. 
     SET @ParameterList = '*Caution: overflow!*' + @ParameterList 
    ELSE 
     SET @ParameterList = LEFT (@ParameterList, @ListLen-1) -- Kill trailing comma. 

    RETURN @ParameterList 
END 
GO 

.
Ensuite, cette requête:

SELECT 
    COUNT (ID) AS NumIDs, 
    NumParams, 
    ParamList 
FROM 
    (
     SELECT 
      ID, 
      COUNT (Parameter)     AS NumParams, 
      dbo.MakeParameterListFor_tblID (ID) AS ParamList 
     FROM 
      tbl 
     GROUP BY 
      ID 
    ) AS ParamsByID 
GROUP BY 
    ParamsByID.ParamList, 
    ParamsByID.NumParams 
ORDER BY 
    NumIDs  DESC, 
    NumParams DESC, 
    ParamList ASC 

.
Donnez ce que vous avez demandé.
Résultats:

NumIDs NumParams ParamList 
    2   2   A, B 
    1   2   C, D 
    1   1   *null*, A 
    1   1   A 
    1   0   *null*