Wie wählt man jede n-te Zeile aus MySQL aus?

79

Ich habe eine Reihe von Werten in einer Datenbank, die ich ziehen muss, um ein Liniendiagramm zu erstellen. Da ich keine hohe Auflösung benötige, möchte ich die Daten neu abtasten, indem ich jede 5. Zeile aus der Datenbank auswähle.

Corban Brook
quelle

Antworten:

83
SELECT * 
FROM ( 
    SELECT 
        @row := @row +1 AS rownum, [column name] 
    FROM ( 
        SELECT @row :=0) r, [table name] 
    ) ranked 
WHERE rownum % [n] = 1 
Taylor Leese
quelle
5
Kann jemand weitere Informationen darüber geben, wie dies funktioniert? Zum Beispiel die Frage, die für jede 5. Zeile gestellt wird, und in der Antwort wird 5 nicht erwähnt.
Crazometer
4
@Crazometer [n]in der Abfrage durch 5 ersetzen , um jede 5. Zeile zu erhalten.
Benjamin Manns
Was wäre, wenn Sie nicht mit der ersten, sondern mit der zweiten Zeile beginnen möchten?
HPWD
@HPWD würden Sie ersetzen @row :=0mit@row :=2
Binar Web
@BinarWeb nein, würden Sie das ändern = 1zu= 2
ysth
55

Sie könnten Mod 5 versuchen , um Zeilen zu erhalten, bei denen die ID ein Vielfaches von 5 ist. (Angenommen, Sie haben eine Art ID-Spalte, die sequentiell ist.)

select * from table where table.id mod 5 = 0;
Owen
quelle
19
Angenommen, Sie haben keine Lücken in der Sequenz aufgrund von Löschen oder Zurücksetzen.
Bill Karwin
3
Dies würde größtenteils funktionieren, berücksichtigt jedoch nicht gelöschte Zeilen.
Corban Brook
2
Einfach und brillant für einige Tests :-)
Rickard Liljeberg
1
Dies ist sinnvoll, wenn Ihre Auswahl alle Daten abruft. Wenn Sie zusätzliche Kriterien in Ihrer Auswahl haben, ist es schwierig zu sagen, welche Daten (falls vorhanden) abgerufen werden sollen.
j_kubik
24

Da Sie sagten, dass Sie MySQL verwenden, können Sie Benutzervariablen verwenden , um eine fortlaufende Zeilennummerierung zu erstellen. Sie müssen dies jedoch in eine abgeleitete Tabelle (Unterabfrage) einfügen.

SET @x := 0;
SELECT *
FROM (SELECT (@x:=@x+1) AS x, mt.* FROM mytable mt ORDER BY RAND()) t
WHERE x MOD 5 = 0;

Ich habe hinzugefügt ORDER BY RAND(), um eine pseudozufällige Stichprobe zu erhalten, anstatt zuzulassen, dass jedes Mal jede fünfte Zeile der ungeordneten Tabelle in der Stichprobe enthalten ist.


Ein anonymer Benutzer versucht , zu bearbeiten , dies zu ändern , x MOD 5 = 0um x MOD 5 = 1. Ich habe es wieder auf mein Original geändert.

Für die Aufzeichnung kann in diesem Zustand ein beliebiger Wert zwischen 0 und 4 verwendet werden, und es gibt keinen Grund, einen Wert einem anderen vorzuziehen.

Bill Karwin
quelle
Ich habe meine Antwort darauf aktualisiert und du hast mich geschlagen! Gute Idee.
Josh Stodola
1
Leider verlangsamt dies die Ausführung um mindestens x100, wenn mit vielen Einträgen gearbeitet wird
phil294
10
SET @a = 0;
SELECT * FROM t where (@a := @a + 1) % 2 = 0;
Andrey Kon
quelle
Dies eignet sich hervorragend zum Partitionieren einer beliebigen schreibgeschützten Tabelle für die parallele Verarbeitung der Zeilen, und die Syntax ist sehr einfach zu lesen und zu verstehen. Sie müssen nur ein ORDER BY in die Primärschlüsselspalte einfügen, um sicherzustellen, dass jede Zeile nur einmal zurückgegeben wird.
Humbads
2

Ich hatte nach so etwas gesucht. Die Antwort von Taylor und Bill veranlasste mich, ihre Ideen zu verbessern.

Die Tabelle data1 hat die Felder read_date, den Wert, den wir für jeden 2D-Datensatz aus einer Abfrage auswählen möchten, die durch einen read_date-Bereich begrenzt ist. Der Name der abgeleiteten Tabelle ist willkürlich und wird hier als DT bezeichnet

Abfrage:

 SET @row := 0;
  SELECT * FROM  ( SELECT @row := @row +1 AS rownum, read_date, value  FROM data1  
  WHERE  read_date>= 1279771200 AND read_date <= 1281844740 ) as DT WHERE MOD(rownum,2)=0
Mark Richards
quelle
Danke, ich habe danach gesucht. Ich musste irgendwie überprüfen, ob eine bestimmte Spalte in einer Protokolltabelle für gespeicherte Prozeduren jedes zweite Mal den gleichen Wert hatte. Wie "Proc beginnt", "Proc endet". Die unten stehende SQL ergibt 1, wenn alles in Ordnung ist. SET @row := 0; SELECT count(distinct Message) FROM ( SELECT @row := @row +1 AS rownum, Message FROM operations.EventLog WHERE LogTime > now() - interval 6 hour and ProcedureName = 'Do_CDR' ) as DT WHERE MOD(rownum,2)=0;
Eigil
1

Sie können diese Abfrage verwenden,

set @n=2; <!-- nth row -->
select * from (SELECT t.*, 
       @rowid := @rowid + 1 AS ID
  FROM TABLE t, 
       (SELECT @rowid := 0) dummy) A where A.ID mod @n = 0;

oder Sie können n durch Ihren n-ten Wert ersetzen

Mohideen bin Mohammed
quelle
1
SELECT *
FROM ( 
    SELECT @row := @row +1 AS rownum, posts.*
    FROM (
        SELECT @row :=0) r, posts
    ) ranked
WHERE rownum %3 = 1

Wo Beiträge ist mein Tisch.

Gor
quelle
1

Wenn Sie MariaDB 10.2, MySQL 8 oder höher verwenden, können Sie dies effizienter tun, und ich denke klarer, indem Sie allgemeine Tabellenausdrücke und Fensterfunktionen verwenden .

WITH ordering AS (
  SELECT ROW_NUMBER() OVER (ORDER BY name) AS n, example.* 
    FROM example ORDER BY name
)
SELECT * FROM ordering WHERE MOD(n, 5) = 0;

Konzeptionell wird dadurch eine temporäre Tabelle mit dem Inhalt der examplenach nameFeld geordneten Tabelle erstellt , ein zusätzliches Feld namens nZeilennummer hinzugefügt und dann nur die Zeilen mit Zahlen abgerufen, die genau durch 5 teilbar sind, dh jede 5. Zeile. In der Praxis kann das Datenbankmodul dies häufig besser optimieren. Aber selbst wenn es nicht weiter optimiert wird, ist es meiner Meinung nach klarer, als Benutzervariablen iterativ zu verwenden, wie Sie es in früheren Versionen von MySQL getan haben.

Richard Smith
quelle
0

Wenn Sie die Zeilennummer in der Ergebnismenge nicht benötigen, können Sie die Abfrage vereinfachen.

SELECT 
    [column name] 
FROM
    (SELECT @row:=0) temp, 
    [table name] 
WHERE (@row:=@row + 1) % [n] = 1 

Ersetzen Sie die folgenden Platzhalter:

  1. Ersetzen Sie [column name]durch eine Liste der Spalten, die Sie abrufen müssen.
  2. Ersetzen Sie [table name]durch den Namen Ihrer Tabelle.
  3. Durch [n]eine Nummer ersetzen . Wenn Sie beispielsweise jede 5. Reihe benötigen, ersetzen Sie sie durch 5
Dharman
quelle
Danke, es ist nah, aber Sie sollten dies besser tun: Wählen Sie den Namen aus (SELECT @row: = - 1) temp, t wobei (@row: = @ row + 1)% 1 = 0; Dies hat zwei Vorteile. Erstens erhalten Sie unabhängig von n immer die erste Zeile und zweitens, wenn Sie n = 1 machen, erhalten Sie alle Werte und keine. (Zwei Änderungen: -1 in Zeile: = - 1 und n = 0 anstelle von n = 1)
Bruce