2016-04-02 12 views
5

Ich versuche, genau 6 zufällige 'Unterhaltung' Einträge anzuzeigen, aber mit meiner aktuellen Abfrage bekommt es eine zufällige Zahl zwischen 1 und 6 und zeigt diese Anzahl an Einträgen an. Wie aktualisiere ich diese Abfrage, um genau 6 zufällige Unterhaltungseinträge aus meiner Artikeltabelle anzuzeigen? Außerdem möchte ich ORDER BY RAND() nicht machen, weil mein Tisch in der nächsten Zeit größer wird. Hier ist meine aktuelle Abfrage:Besser als die aktuelle Abfrage, um zufällige kategorisierte Einträge zusammenzustellen?

SELECT 
    r1.* 
FROM 
    Articles AS r1 
    INNER JOIN (SELECT(RAND() * (SELECT MAX(id) FROM Articles)) AS id) AS r2 
WHERE 
    r1.id >= r2.id 
    AND r1.category = 'entertainment' 
LIMIT 6; 

Tabellenstruktur:

table Articles 
- id (int) 
- category (varchar) 
- title (varchar) 
- image (varchar) 
- link (varchar) 
- Counter (int) 
- dateStamp (datetime) 
+0

Haben Sie Lücken in Ihrer 'id' Spalte? –

+0

@PaulSpiegel Ja, zum Beispiel ID: 1 wird als Lebensstil kategorisiert, ID: 2 wird als Unterhaltung kategorisiert, ID: 3 wird als Wissenschaft kategorisiert und so weiter. Wenn die Abfrage alle Unterhaltungskategorien erfasst, sind alle diese IDs Nummern mit Lücken zwischen ihnen. – user2896120

Antwort

0

Mit

select floor(rand() * m.maxId + 1) as randomId 
from Articles a 
join (SELECT MAX(id) maxId FROM Articles) m 
limit 100 

erhalten Sie 100 zufällige IDs erstellen. Ich nehme 100, weil Sie Lücken in Ihrer ID-Spalte haben, also wird die Wahrscheinlichkeit, dass Sie nicht genug vorhandene IDs erhalten, (sehr) klein sein. Dann können Sie dieses Ergebnis verwenden, um nur 6 Zeilen mit dieser IDs zu wählen:

select distinct a.* 
from (
    select id, floor(rand() * m.maxId + 1) as randomId 
    from Articles a 
    join (SELECT MAX(id) maxId FROM Articles) m 
    limit 100 
) r 
join Articles a on a.id = r.randomId 
order by r.id -- only need it for small tables. will slow down the query on big tables 
limit 6 

Der beste Wert für LIMIT im subselect hängt von Prozentsatz von Lücken in Ihrem ids. 100 sollte ausreichen und schnell sein.

aktualisieren

Wenn Sie mit dem category filtern müssen Sie eine WHERE a.category = 'entertainment' Klausel vor ORDER BY und LIMIT hinzufügen können. In diesem Fall müssen Sie jedoch die Anzahl der generierten zufälligen IDs anpassen.

Zum Beispiel: Wenn Sie 1M Artikel eingefügt haben, aber 10% davon gelöscht wurden, dann gibt es im Durchschnitt 90 zufällig generierte IDs. Wenn jetzt 10% der Artikel category = 'entertainment' haben, entspricht ein Durchschnitt von 9 zufälligen Zeilen der Bedingung. Durchschnitt bedeutet - es könnte 3 sein und könnte auch 16 sein. Also müssen Sie mehr zufällige IDs generieren, um sicher zu sein, dass Sie mindestens 6 Artikel erhalten. Mit LIMIT 1000 im Subselect erhalten Sie durchschnittlich 90 zufällige Unterhaltungsartikel. Auf diese Weise ist es sehr unwahrscheinlich, dass Sie weniger als 6 bekommen. Sie müssen also die Statistiken Ihrer Tabelle kennen, um eine gute LIMIT auszuwählen.

Ein weiteres Problem mit der Klausel WHERE ist, dass MySQL die Join-Reihenfolge möglicherweise umkehren kann, um einen Index zum Filtern zu verwenden. Dies kann für eine kleine Anzahl generierter Zufalls-IDs schneller sein, ist jedoch möglicherweise langsamer, wenn die LIMIT im Subselect sehr groß ist. Sie können die Verknüpfungsreihenfolge erzwingen, indem Sie STRIGHT_JOIN anstelle von JOIN verwenden - aber in meinem Test mit LIMIT 10000 hat es keinen messbaren Unterschied gemacht.

Wenn Ihr Zustand zu selektiv ist (z. B. nur 1% der Artikel haben category='entertainment'), kann ein einfacher ORDER BY RAND() schneller sein, weil Sie sonst zu viele zufällige IDs erstellen müssten. Aber bis zu 10K Zeilen, die zu Ihrem Zustand ORDER BY RAND() passen, sind schnell genug.

+0

So wird es nur die ersten 100 IDs bekommen? Was ist, wenn ich mehr als 100 IDs habe? Auch will ich Unterhaltungseinträge bekommen, diese Abfrage spezifiziert nicht in der WHERE – user2896120

+0

Nein .. es erzeugt 100 zufällige IDs. Sie können eine beliebige andere Tabelle als "Artikel" für den Subselect mit mindestens 100 Zeilen verwenden. Sie müssen nur einen Satz von 100 Dummy-Zeilen erstellen, um jeweils eine Zufallszahl zu generieren. –

+0

Ist der Parameter @preLimit in der Abfrage enthalten? Auch, was ist das für in der Abfrage? – user2896120

0

Ihre 'Unterhaltung' Einträge sollten alle eindeutige IDs haben, die ganze Zahlen sein sollten.

Wenn dies der Fall ist, könnten Sie 6 zufällige Ints zwischen 1 und die Menge der Einträge, die Sie mit PHP rand() -Funktion haben. Hier ist eine Funktion, die ich geschrieben habe, die nützlich sein könnte.

function selectSixRandomEntries() { 
    $queryWhere = ""; 
    $i = 0; 

    while($i < 6) { 
     $randomNumber = rand(1, 200); 
     if (strpos($queryWhere, $randomNumber) == -1) 
      continue; 

     $queryWhere .= "r1.id = " . rand(1, 200); 
     if ($i != 5) 
      $queryWhere .= " OR "; 

     $i++; 
    } 

    return $queryWhere 
} 

Und, es zu benutzen Sie könnten versuchen,

$query = "SELECT 
    r1.* 
FROM 
    Articles AS r1 
    INNER JOIN (SELECT(RAND() * (SELECT MAX(id) FROM Articles)) AS id) AS r2 
WHERE 
    " . selectSixRandomEntries() . " 
    AND r1.category = 'entertainment' 
LIMIT 6"; 
+0

Aus irgendeinem Grund, ich Ich bekomme eine leere Seite, nachdem ich den Code eingefügt und angepasst habe, den Teil der Abfrage, in dem "WHERE" steht. selectSixRandomEntries(). "ist das genau so, wie es angelegt werden soll? – user2896120

+0

Es sollte '=' anstatt '==' und 'ODER' statt' AND' sein –

+0

Danke Paul, ich habe es jetzt behoben. – Acidic