2008-12-05 7 views
20

ich eine Tabelle wie folgt aus:Erhalten gängigste Wert für jeden Wert einer anderen Spalte in SQL

Column | Type | Modifiers 
---------+------+----------- 
country | text | 
food_id | int | 
eaten | date | 

Und für jedes Land, ich möchte das Essen bekommen, die am häufigsten gegessen wird. Das Beste, was ich mir vorstellen kann (ich bin mit Postgres) ist:

CREATE TEMP TABLE counts AS 
    SELECT country, food_id, count(*) as count FROM munch GROUP BY country, food_id; 

CREATE TEMP TABLE max_counts AS 
    SELECT country, max(count) as max_count FROM counts GROUP BY country; 

SELECT country, max(food_id) FROM counts 
    WHERE (country, count) IN (SELECT * from max_counts) GROUP BY country; 

In dieser letzten Aussage, die GROUP BY und max() nötig sind, um Bindungen zu brechen, wo zwei verschiedene Lebensmittel die gleiche Anzahl haben.

Dies scheint wie viel Arbeit für etwas konzeptionell einfach. Gibt es einen einfacheren Weg, es zu tun?

Antwort

2

so etwas wie dieses

select country, food_id, count(*) cnt 
into #tempTbl 
from mytable 
group by country, food_id 

select country, food_id 
from #tempTbl as x 
where cnt = 
    (select max(cnt) 
    from mytable 
    where country=x.country 
    and food_id=x.food_id) 

Versuchen Dies alles in einem einzigen Auswahl genommen werden konnte, aber ich habe keine Zeit mit ihm im Augenblick zu vertrödeln haben.

Viel Glück.

3

Hier ist, wie es zu tun, ohne temporäre Tabellen:

Edit: vereinfachte

select nf.country, nf.food_id as most_frequent_food_id 
from national_foods nf 
group by country, food_id 
having 
    (country,count(*)) in ( 
         select country, max(cnt) 
         from 
          (
          select country, food_id, count(*) as cnt 
          from national_foods nf1 
          group by country, food_id 
         ) 
         group by country 
         having country = nf.country 
         ) 
+0

ich Interesse hätte, den Plan für diese Ausführung gegenüber der temporären Tabelle zu sehen - diese „mit“ Klauseln ausgewertet Reihen _after_ wählen abruft passende , Recht? Scheint so, als könnte es eine Bootsladung von extra IO geben. –

+0

Es gibt ein paar vollständige Tabellen-Scans im Plan, ja. – JosephStyons

7
SELECT DISTINCT 
"F1"."food", 
"F1"."country" 
FROM "foo" "F1" 
WHERE 
"F1"."food" = 
    (SELECT "food" FROM 
     (
      SELECT "food", COUNT(*) AS "count" 
      FROM "foo" "F2" 
      WHERE "F2"."country" = "F1"."country" 
      GROUP BY "F2"."food" 
      ORDER BY "count" DESC 
     ) AS "F5" 
     LIMIT 1 
    ) 

Nun, ich schrieb dies in Eile und habe es nicht wirklich gut überprüfen. Die Unterauswahl ist möglicherweise ziemlich langsam, aber das ist die kürzeste und einfachste SQL-Anweisung, die ich mir vorstellen kann. Ich werde wahrscheinlich mehr erzählen, wenn ich weniger betrunken bin.

PS: Na ja, "foo" ist der Name meiner Tabelle, "Essen" enthält den Namen des Essens und "Land" den Namen des Landes. Beispielausgabe: (.) ​​

food | country 
-----------+------------ 
Bratwurst | Germany 
Fisch  | Frankreich 
+0

Das müssen an den meisten Orten einfache Anführungszeichen sein, denke ich. – ocket8888

3
SELECT country, MAX(food_id) 
    FROM(SELECT m1.country, m1.food_id 
      FROM munch m1 
     INNER JOIN (SELECT country 
          , food_id 
          , COUNT(*) as food_counts 
         FROM munch m2 
        GROUP BY country, food_id) as m3 
       ON m1.country = m3.country 
     GROUP BY m1.country, m1.food_id 
     HAVING COUNT(*)/COUNT(DISTINCT m3.food_id) = MAX(food_counts)) AS max_foods 
    GROUP BY country 

Ich mag nicht über die MAX GROUP BY Bindungen zu brechen ... Es muss doch einen Weg geben, gegessen Datum in die in irgendeiner Weise JOIN zu integrieren, um willkürlich die Auswahl neuste ...

Ich bin an dem Abfrageplan für dieses Ding interessiert, wenn Sie es auf Ihren Live-Daten ausführen!

3
select country,food_id, count(*) ne 
from food f1 
group by country,food_id  
having count(*) = (select max(count(*)) 
        from food f2 
        where country = f1.country 
        group by food_id) 
5

versuchen Sie dies:

Select Country, Food_id 
From Munch T1 
Where Food_id= 
    (Select Food_id 
    from Munch T2 
    where T1.Country= T2.Country 
    group by Food_id 
    order by count(Food_id) desc 
     limit 1) 
group by Country, Food_id 
12

PostgreSQL Unterstützung in 8.4 für window functions eingeführt, ein Jahr nach dieser Frage gestellt wurde. Es ist erwähnenswert, dass es heute wie folgt gelöst werden könnte:

SELECT country, food_id 
    FROM (SELECT country, food_id, ROW_NUMBER() OVER (PARTITION BY country ORDER BY freq DESC) AS rn 
      FROM ( SELECT country, food_id, COUNT('x') AS freq 
        FROM country_foods 
       GROUP BY 1, 2) food_freq) ranked_food_req 
WHERE rn = 1; 

Das obige wird Bindungen brechen. Wenn Sie Bindungen nicht trennen möchten, können Sie stattdessen DENSE_RANK() verwenden. Hier

+1

Dank dafür, habe ich den gleichen Ansatz mit T-SQL in MS SQL Server verwendet. Eine wirklich elegante Lösung. – niallsco

1

ist eine Aussage, die ich glaube, gibt Ihnen, was Sie wollen, und ist einfach und prägnant:

select distinct on (country) country, food_id 
from munch 
group by country, food_id 
order by country, count(*) desc 

Bitte lassen Sie mich wissen, was Sie denken.

BTW, die distinct auf Funktion ist nur in Postgres verfügbar.

Beispiel, Quelldaten:

country | food_id | eaten 
US  1   2017-1-1 
US  1   2017-1-1 
US  2   2017-1-1 
US  3   2017-1-1 
GB  3   2017-1-1 
GB  3   2017-1-1 
GB  2   2017-1-1 

Ausgang:

country | food_id 
US  1 
GB  3 
+0

Wenn Sie nach all dieser Zeit eine neue Antwort vorschlagen, empfehle ich, es an einer Beispieltabelle zu versuchen und die Ergebnisse zu posten. Bitte erwähnen Sie auch, welchen Datenbankserver Sie verwenden (mysql oder was auch immer). – ToolmakerSteve

+1

Die Funktion _distinct on_ ist nur in Postgres verfügbar, daher bin ich mir nicht sicher, wie Sie das in einer anderen Datenbank tun würden. OP verwendet Postgres, so scheint es angemessen. Ich schrieb dies mithilfe der Datenbanktabelle, die von op namens _munch_ vorgeschlagen wurde, die drei Felder hat: Land (Text), food_id (int) und gegessen (Datum) – user2247323

Verwandte Themen