2009-12-30 8 views
1

J'ai besoin d'obtenir les valeurs maximum et minimum, mais j'ai aussi besoin d'obtenir l'identifiant de ligne de ces maximum ou minimum sur la même ligne.Minimum de requête SQL maximum

SELECT MIN([Value]), MAX([Value]), id 
FROM [AnalystEstimates].[dbo].[AnalystEstimateValues] 
GROUP BY indicatorid 
+0

Avez-vous besoin de l'ID et MIN, puis l'ID et Max. En 2 rangs? à côté de l'autre? –

+0

oui ce serait cool si ID et MIN, puis l'ID et Max sont les uns à côté des autres – Woland

+2

@Woland: Pourriez-vous modifier votre question pour refléter l'exigence modifiée? –

Répondre

3

On ne sait pas très bien ce que vous voulez de votre question. Voulez-vous vraiment le code d'identification GROUP BY? Sinon, c'est assez simple et vous avez déjà beaucoup de réponses. Mais si vous voulez GROUP BY alors c'est plus difficile et personne ne l'a encore tout à fait raison. Je suppose également que vous ne voulez qu'une ligne par indicateur, et s'il y a des lignes dupliquées qui ont la même valeur max/min, il est préférable de simplement en choisir une arbitrairement au lieu de retourner les deux.

Voilà ma tentative, en utilisant CTEs (nécessite SQL Server 2005 ou plus récent):

WITH 
    RowNumbers AS (
     SELECT ROW_NUMBER() OVER (ORDER BY indicatorid, value) AS RowNumber, * 
     FROM [AnalystEstimates].[dbo].[AnalystEstimateValues]), 
    MinRowNumbers AS (
     SELECT indicatorid, MIN(RowNumber) AS RowNumber FROM RowNumbers GROUP BY indicatorid), 
    MaxRowNumbers AS (
     SELECT indicatorid, MAX(RowNumber) AS RowNumber FROM RowNumbers GROUP BY indicatorid) 
SELECT 
    MinRowNumbers.indicatorid, 
    RN1.Value AS MinValue, 
    RN1.ID AS MinValueId, 
    RN2.Value AS MaxValue, 
    RN2.ID AS MaxValueId 
FROM MinRowNumbers 
JOIN MaxRowNumbers ON MinRowNumbers.indicatorid = MaxRowNumbers.indicatorid 
JOIN RowNumbers RN1 ON MinRowNumbers.RowNumber = RN1.RowNumber 
JOIN RowNumbers RN2 ON MaxRowNumbers.RowNumber = RN2.RowNumber 

Voici quelques données que je l'habitude de le tester:

CREATE TABLE AnalystEstimateValues (ID int, indicatorid int, Value int); 

INSERT INTO AnalystEstimateValues (ID, indicatorid , Value) VALUES 
(1, 1, 4), 
(2, 1, 4), 
(3, 2, 6), 
(4, 1, 2), 
(5, 2, 2), 
(6, 2, 5), 
(7, 3, 0); 

Et voici la sortie que je reçois:

indicatorid MinValue MinValueId MaxValue MaxValueId 
      1  2   4  4   2 
      2  2   5  6   3 
      3  0   7  0   7 

Si ce n'est pas ce que vous voulez, pouvez-vous s'il vous plaît essayer d'améliorer votre question pour nous dire ce que vous voulez?


Mise à jour: Voici une solution alternative basée sur la réponse de Craig jeune mais avec des jointures au lieu de sous-requêtes:

WITH 
    UniqueIds AS (
     SELECT IndicatorId, Value, MIN(id) AS Id 
     FROM AnalystEstimateValues 
     GROUP BY IndicatorId, Value) 
SELECT 
    lims.IndicatorId, 
    MinValue, 
    T1.Id AS MinValueId, 
    MaxValue, 
    T2.Id AS MaxValueId 
FROM (
     SELECT 
      IndicatorId, 
      MIN(Value) as MinValue, 
      MAX(Value) as MaxValue 
     FROM AnalystEstimateValues 
     GROUP BY IndicatorId) lims 
JOIN UniqueIds T1 ON lims.IndicatorId = T1.IndicatorId AND lims.MinValue = T1.Value 
JOIN UniqueIds T2 ON lims.IndicatorId = T2.IndicatorId AND lims.MaxValue = T2.Value 

C'est plus propre et probablement aussi plus rapide que ma première version, bien que je ne l'ai pas courir tests de performance pour vérifier cela.

+0

je vous remercie c'est – Woland

+0

OK, j'ai eu de la chance. :) Je pense que la prochaine fois, vous devriez passer un peu plus de temps sur votre question afin que les réponses correspondent mieux à ce que vous voulez. En fait, je ne pense pas que ce soit la meilleure façon de résoudre ce problème - cela pourrait être trop lent. Je vois que vous l'avez déjà accepté, mais j'espère que vous avez confirmé qu'il est assez rapide pour vos données avant de le faire. Si quelqu'un peut plus tard résoudre ce problème d'une meilleure façon maintenant qu'il peut voir ce que vous voulez, alors vous devriez accepter sa réponse à la place de celle-ci. –

+0

Je ne connais pas la syntaxe que vous avez utilisée; Je dois lire à ce sujet. En tant que note de performance, vous avez 2 sous-requêtes GROUP BY - une pour MIN et l'autre pour MAX. Vous devriez être capable de les combiner en une seule sous-requête et obtenir une amélioration significative des performances. –

0

Votre requête retournerait le max/min pour chaque ID que vous regroupez par l'ID. Essayez quelque chose comme ça

SELECT tblFoo.ID, tblFoo.Value 
FROM tblFoo 
WHERE (((tblFoo.Value)=(SELECT MAX([tblFoo]![Value]) FROM tblFoo))) 
    OR (((tblFoo.Value)=(SELECT MIN([tblFoo]![Value]) FROM tblFoo))); 
+1

Vous aussi, vous avez perdu la construction de regroupement. –

0
SELECT TOP 1 
    ID, 
    'min' as type, 
    value 
FROM 
    AnalystEstimateValues 
WHERE 
    value = (select min(value) from AnalystEstimateValues) 
UNION 
    SELECT TOP 1 
    ID, 
    'max' as type, 
    value 
FROM 
    AnalystEstimateValues 
WHERE 
    value = (select max(value) from AnalystEstimateValues) 
+1

Vous aussi, vous avez perdu la construction de regroupement. –

1

Si Min = Max, vous l'obtenir même ID (il peut être attaché valeurs Min et Max)

Dans le cas contraire, il est/sont ID (s par valeur min/max.

Vous pouvez avoir 2 lignes ou 4 colonnes, fondamentalement.

SELECT 
    Mn.ID, foo.MinVal, 
    Mx.ID, foo.MaxVal  
FROM 
    (
    SELECT 
     MIN([Value]) AS MinVal, 
     MAX([Value]) AS MaxVal, 
     indicatorid 
    FROM 
     [AnalystEstimates].[dbo].[AnalystEstimateValues] 
    GROUP BY 
     indicatorid 
    ) foo 
    JOIN 
    [AnalystEstimates].[dbo].[AnalystEstimateValues] Mn ON 
      foo.MinVal = Mn.[Value] AND foo.indicatorid = Mn.indicatorid 
    JOIN 
    [AnalystEstimates].[dbo].[AnalystEstimateValues] Mx ON 
      foo.MaxVal = Mx.[Value] AND foo.indicatorid = Mx.indicatorid 

Edit:

Les solutions TOP ne vous donnera pas les lignes où vous avez égalité MIN/MAX valeurs définies avec moins TIES

+0

ON foo.MinVal = Mn. [Valeur] et ON foo.MaxVal = Mx. [Valeur] n'est pas possible car il peut y avoir des valeurs en double – Woland

+0

Alors: quel ID voulez-vous s'il y a des doublons? Comment décideriez-vous une identité sur une autre? – gbn

+0

Si vous souhaitez autoriser le renvoi de plusieurs résultats comme celui-ci, vous devez inclure l'indicateur dans la jointure, sinon vous pourriez récupérer des lignes du mauvais groupe. C'est à dire. vous avez besoin de ceci: 'ON foo.MinVal = Mn. [Valeur] AND foo.indicatorid = Mn.indicatorid'. –

0

Cela pourrait le faire, mais je n'ai pas MSSQL donc je ne peux pas le tester. Surtout les crochets peuvent avoir besoin de peaufiner. A part cela, il devrait être assez standard sql et fait ce que vous voulez.

Il obtient toutes les valeurs min (id/value) et max (id/value) groupées par indicatorid. Dans la même rangée.

SELECT mint.indicatorid, mint.min_id, mint.min_value, maxt.max_id, maxt.max_value 
FROM (
    SELECT indicatorid, id as min_id, MIN([Value]) AS min_value 
    FROM [AnalystEstimates].[dbo].[AnalystEstimateValues] 
    HAVING [Value] = min_value 
    GROUP BY indicatorid 
) mint JOIN (
    SELECT indicatorid, id as max_id, MAX([Value]) AS max_value 
    FROM [AnalystEstimates].[dbo].[AnalystEstimateValues] 
    HAVING [Value] = max_value 
    GROUP BY indicatorid 
) maxt ON mint.indicatorid = maxt.indicatorid 
+1

J'ai cette erreur "ID" n'est pas valide dans la liste de sélection car il n'est pas contenu dans une fonction d'agrégat ou la clause GROUP BY. " – Woland

1

question importante
les données de l'échantillon de Mark Byers montre un scénario que vous devez envisager:

  • IndicatorId = 1 a une valeur maximum de 4.
  • Il y a deux numéros d'identification qui partagent la même valeur maximale (1 & 2).

Alors, lequel des deux ID doit être affiché?

Je l'ai supposé suffisant pour afficher l'ID le plus bas. La requête suivante devrait être la plus efficace et bénéficierait d'un index sur (indicatorid, Value).

SELECT lims.*, 
     (
     SELECT MIN(id) 
     FROM AnalystEstimateValues m 
     WHERE m.IndicatorId = lims.IndicatorId 
      AND m.Value = lims.MinValue 
     ) AS MinId, 
     (
     SELECT MIN(id) 
     FROM AnalystEstimateValues m 
     WHERE m.IndicatorId = lims.IndicatorId 
      AND m.Value = lims.MaxValue 
     ) AS MaxId 
FROM (
     SELECT IndicatorId, 
       MIN(Value) as MinValue, 
       MAX(Value) as MaxValue 
     FROM AnalystEstimateValues 
     GROUP BY IndicatorId 
     ) lims 
+0

Cela produit le résultat correct selon mes exemples de données de test. Il utilise une sous-sélection corrélée, donc je ne suis pas sûr que ce sera toujours rapide - s'il y a un très grand nombre de très petits groupes d'indicateurs, je pense que ce sera plus lent. Mais il est très difficile de deviner ces choses sans l'essayer sur des données plus réalistes. Je pense toujours qu'il doit y avoir une meilleure façon de le faire mais de toute façon. –

+0

J'ai fait une version modifiée de votre solution et l'ai ajoutée à la fin de ma première réponse. Je fais essentiellement la même chose mais en utilisant JOINs au lieu de sous-éléments corrélés. Je suppose que ce sera plus rapide, mais cela nécessitera des tests appropriés pour être sûr. Je n'ai pas l'intention de le faire - c'est trop d'efforts pour créer des données de test réalistes - mais si quelqu'un d'autre le fait (Woland?), Je serais très intéressé de connaître les résultats. –

+0

PS: Je suppose que votre solution est bonne car il n'y a probablement pas autant d'indicateurs. –

Questions connexes