SQL N Records per Group

Guten Tag!

Würde gerne aus einer Datenmenge eine Gruppierung aufbauen, wobei pro Gruppen aber nur maximal die drei ersten Einträge aufgelistet werden sollen. Mein unter SAS nicht laufender Wunsch-SQL-Code wäre also folgender:

SELECT PER GROUP TOP 3
Groupfield, Field_2, Field_3
FROM MyTable
GROUP BY Groupfield

hat da jemand etwas "Idee und Hinweis" für mich wie man dies angehen kann?

Grüße // Martin Schaefer

Geht auch mit SQL und LEFT JOIN

data all (drop=i);
   
do Groupfield=1 to 3;
      
do i=1 to 10;
         Field_2 = ranuni(
2);      
         Field_3 = ranuni(
3);
         
output;
      
end;
   
end;
run;

proc sql noprint;
   
create table TopN as
   
SELECT all.GroupField, all.Field_2, all.Field_3
   
FROM all
   LEFT JOIN all
as a ON (all.GroupField = a.GroupField
     
AND all.Field_2 >= a.Field_2)
   GROUP
BY all.GroupField, all.Field_2, all.Field_3
   HAVING COUNT(*) <=
2;
quit;
Mehr zum Thema SQL und TopN findet man hier

Unterschiede

Die beiden Lösungen führen nicht zu den gleichen Ergebnissen!

Die SQL-TopN-Lösung ermittelt pro Gruppe die drei Datensätze mit den kleinsten Werten bezüglich Field_2. Die Data-Schritt-Lösung hingegen ermittelt die ersten drei Datensätze pro Gruppe ohne Ansehen der Werte. Bei der Data-Schritt-Lösung ist das Ergebnis von der Reihenfolge der Datensätze abhängig, bei der SQL-TopN-Lösung nicht.

Die Anfrage von Martin Schäfer ist in diesem Punkt nicht ganz klar: "Die drei ersten Einträge" kann ja beides bedeuten.

Darf es auch ein Data-Step sein?

/* Beispiel: drei Gruppen à 10 Sätze */
data all (drop=i);
   
do Groupfield=1 to 3;
      
do i=1 to 10;
         Field_2 = ranuni(
2);      
         Field_3 = ranuni(
3);
         
output;
      
end;
   
end;
run;

/* die ersten drei pro Gruppe herausfiltern */
data topthree(drop=count);
   
set all;
   
by Groupfield;
   
if first.Groupfield then count=0;
   count+
1;
   
if count<=3;
run;
Wenn nötig vorher noch sortieren.
Wenn eine nicht nach Groupfield sortierte Datei verarbeitet werden soll, BY mit NOTSORTED verwenden.

Obwohl ich von der

Obwohl ich von der Datenbankseite komme und SQL mir meist mehr liegt, hat mich die markant kurze DataStep- Variante dann überzeugt. Das schöne ist an ihr, dass man mit etwas Abänderung Sie auch zur Doulbettenfilterung verwenden kann.

data work.SC_Data_Input (drop=_count_  );
   
set work.SC_Data_Input;          
   
by _SID_Temp_  NOTSORTED ;
   
if   (  first._SID_Temp_ ) then _count_ = 0;
   _count_  +
1;
   
if ( _count_  > 1  ) then delete;
run;

Ja vielen Dank für die Lösungen // Martin Schäfer

kleine Vereinfachung

Darf ich noch eine kleine Vereinfachung vorschlagen?

data work.SC_Data_Input;
   
set work.SC_Data_Input;          
   
by _SID_Temp_ NOTSORTED;
   
if first._SID_Temp_;
run;

In der Kürze liegt die

Gelegneheit den Faden nochmal aufzunehmen. der DataStep ist schon markant effizient. Mit etwas Experimentieren ob das auch mit mehreen Feldern als Gruppenschlüssel geht, bin ich dann zu dem Schluss gekommen, dass das By hier nicht wirklich weiterhilft. Also habe ich das Gruppieren nach einem Gesamtschlüssel über mehrere Felder mit Concatieren gelöst:

_SID_temp = Feld1 || Feld2 || Feld3

Damit können dann beide obigen Lösungen zum Top n per Gruppe und dem Douppletttenfiltern mit den vorgestellten Routinen erreicht werden.

Viele Grüße

warum nicht mehrere BY-Variablen?

data work.SC_Data_Input;
   
set work.SC_Data_Input;          
   
by Feld1 Feld2 Feld3 NOTSORTED;
   
if first.Feld3;
run;

geht natürlich auch..

Mehrere By Variablen habe ich nicht verwendet, da bei mir die einzelnen Variablen in einem relativ großen DataSet verstreut liegen. Durch das Concatieren hängt SAS eine Variable rechts an den DataSet an. Das ist bei der optischen Kontrolle hilfreich. Vom Ergebnis her ist das natürlich kein Unterschied.

(Sorry für die Antwortpause, aber mein Urlaub wartete auf mich...)

Viele Grüße // Martin Schäfer