WN

WN (https://www.wn.se/forum/index.php)
-   Serversidans teknologier (https://www.wn.se/forum/forumdisplay.php?f=4)
-   -   MySQL, random + biased/weighted mot äldsta? (https://www.wn.se/forum/showthread.php?t=1061181)

JesperA 2014-03-13 08:45

MySQL, random + biased/weighted mot äldsta?
 
Skulle behöva en skön & enkel query som väljer ut random rader i databasen men "väger" mer mot dom äldre raderna då det är viktigt att dom äldsta raderna inte "glöms" bort.

Tabellen innehåller fler kolumner än det jag nämner, men det är dom kolumnerna jag har att använda för queryn, tyvärr. Har googlat på biased & weighted queries i MySQL men dom flesta jag hittat använder en tabell med "score", "multiplier" osv för att använda som "tyngd" i queryn, någon sådan har jag alltså inte.

Kolumnerna som skall användas för detta är:

Kod:

CREATE TABLE `TestTable` (
  `PostID` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `PostTitle` varchar(250) DEFAULT NULL,
  `PostDate` datetime DEFAULT NULL,
  PRIMARY KEY (`PostID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Prestandaproblemen med en random query är inget problem i mitt fall, tabellen innehåller inte tillräckligt med rader för att vara ett problem.

Skulle vilja få ut 10st random rader från hela tabellen men med mer vikt mot dom äldre, några förslag på en smidig lösning?

Kimppa 2014-03-13 09:00

En enklare lösning skulle väl kunna vara att göra följande i ett programmeringsspråk:

- Räkna antalet rows i tabellen.
- Slumpa tal mellan 0 och antalet rows i tabellen.
- Skriv ut och sortera efter "datum DESC" så de äldsta syns först.

Vet tyvärr inte om det går att göra i SQL direkt (kanske går).

JesperA 2014-03-13 09:15

Citat:

Ursprungligen postat av Kimppa (Inlägg 20488260)
En enklare lösning skulle väl kunna vara att göra följande i ett programmeringsspråk:

- Räkna antalet rows i tabellen.
- Slumpa tal mellan 0 och antalet rows i tabellen.
- Skriv ut och sortera efter "datum DESC" så de äldsta syns först.

Vet tyvärr inte om det går att göra i SQL direkt (kanske går).

Tyvärr, det skulle inte ge viktad/biased output, det är i princip bara en helt vanlig "ORDER BY rand(), PostDate ASC" i MySQL, således har varenda rad i databasen lika stor chans att visas.

Johnny Viking 2014-03-13 09:25

I väntan på en SQL-expert som kan skapa en funktion som löser det åt dig så skulle du ju kunna splitta upp det hela i t.ex 3 queries som du mergar i efterhand. T.ex query 1 med "äldre än X", och 2 "äldre än x" osv. På så sätt styr du själv på ett ungefär det förväntade resultatet.

tartareandesire 2014-03-13 09:25

Du gör nog bäst i att köra viktningen i scriptet. Det blir mer flexibelt och när du har så få rader så kan du som du säger göra lite som du själv vill med datan. Alternativt kan du ju bara lägga in ett extra viktningsfält i databasen som du uppdaterar en gång per dygn eller liknande beroende på hur ofta din tabell ändras.

HenrikAI 2014-03-13 09:30

Något i stil med detta, kanske:

Kod:

SELECT col1, col2, ..., -RAND()*UNIX_TIMESTAMP(PostDate) AS random_value
FROM TestTable
ORDER BY random_value ASC

Vet inte hur bra det är rent prestandamässigt dock.

Annars kan du lösa det med lite kod i ditt favoritspråk. Ta ut ett slumptal, t.ex. mellan 2000 och 2013 (eller vad du nu har för årtal på raderna i databasen), och kör något i stil med:

Kod:

SELECT col1, col2, ...
FROM TestTable
WHERE YEAR(PostDate) <= $slumptal
ORDER BY RAND()


Nerix 2014-03-13 09:51

http://stackoverflow.com/questions/1...-probabilities

JesperA 2014-03-13 15:47

Tack för svaren


Citat:

Ursprungligen postat av Johnny Viking (Inlägg 20488263)
I väntan på en SQL-expert som kan skapa en funktion som löser det åt dig så skulle du ju kunna splitta upp det hela i t.ex 3 queries som du mergar i efterhand. T.ex query 1 med "äldre än X", och 2 "äldre än x" osv. På så sätt styr du själv på ett ungefär det förväntade resultatet.

Jao det skulle nog gå rätt bra ja, om jag tänker rätt dvs. Går ju lösa enkelt på min 2 olika sätt i MySQL, antingen med subqueries eller med UNION.

Tex om jag gör 3st "brytpunkter" där jag väljer ut tex 100st rader av dom 33% äldsta raderna, 50st rader av 33% av dom mellersta raderna och 25st av dom 33% senaste raderna i databasen och därefter gör ett random urval på dom sammanställda raderna. Då är ju oddsen större att rand() tar någon av dom 100 äldsta än av dom 25st senaste, skall testa.



Citat:

Ursprungligen postat av tartareandesire (Inlägg 20488264)
Du gör nog bäst i att köra viktningen i scriptet. Det blir mer flexibelt och när du har så få rader så kan du som du säger göra lite som du själv vill med datan. Alternativt kan du ju bara lägga in ett extra viktningsfält i databasen som du uppdaterar en gång per dygn eller liknande beroende på hur ofta din tabell ändras.

Skulle helst inte vilja ha ett viktningsfält då det ökar komplexiten ett redan komplext script & vill helst undvika småupdates så mycket det går då jag kör databasen på ett par SSD:er. Skulle iofs kunna skapa en dedikerad MEMORY tabell just för viktningen så är de ju inget problem egentligen. Sliter ju inte överdrivet extra att göra en update på alla rader en gång om dagen men ändå, varje liten grej för att förlänga livslängden på SSD:erna



Citat:

Ursprungligen postat av HenrikAI (Inlägg 20488266)
Något i stil med detta, kanske:

Kod:

SELECT col1, col2, ..., -RAND()*UNIX_TIMESTAMP(PostDate) AS random_value
FROM TestTable
ORDER BY random_value ASC

Vet inte hur bra det är rent prestandamässigt dock.

Annars kan du lösa det med lite kod i ditt favoritspråk. Ta ut ett slumptal, t.ex. mellan 2000 och 2013 (eller vad du nu har för årtal på raderna i databasen), och kör något i stil med:

Kod:

SELECT col1, col2, ...
FROM TestTable
WHERE YEAR(PostDate) <= $slumptal
ORDER BY RAND()


Hmm, första exemplet vet jag inte riktigt hur det fungerar, ser inte huruvida den lägger extra vikt på äldre eller inte, skall testa.

Exempel 2 skulle nog inte fungera som tänkt för om jag slumpar ett årtal så kommer den bara hämta rader äldre/nyare än slumptalet, alltså alla 10 av dom raderna jag vill ha, men jag vill ha så att 2st av dom 10 skulle kunna vara bland dom 5 senaste raderna i tabellen, 2st i mitten av tabellen och 6st mot dom allra äldsta i tabellen.

Citat:

Ursprungligen postat av Nerix (Inlägg 20488271)

Tack men tror inte att WITH fungerar eller har någon motsvarighet i MySQL och exemplet har ett viktningsfält vilket jag helst skulle vilja slippa om det är möjligt.

Clarence 2014-03-13 16:10

En enkel lösning:
- Räkna antalet rader (select count(*) om du använder myisam, via cachad räknare om du använder innodb)
- I ditt script vikta dina slumpnummer (Google: weighted random generator eller något i den stilen om du behöver en färdig algoritm)
- Kör en select med order by PostDate och LIMIT på dina slumpnummer (Kontrollera huruvida du kan köra unions/subquerys och ändå använda index, med vanilla MySQL bör det nog vara bättre med en query per rad)

tartareandesire 2014-03-13 18:02

Citat:

Ursprungligen postat av JesperA (Inlägg 20488294)
Skulle helst inte vilja ha ett viktningsfält då det ökar komplexiten ett redan komplext script & vill helst undvika småupdates så mycket det går då jag kör databasen på ett par SSD:er. Skulle iofs kunna skapa en dedikerad MEMORY tabell just för viktningen så är de ju inget problem egentligen. Sliter ju inte överdrivet extra att göra en update på alla rader en gång om dagen men ändå, varje liten grej för att förlänga livslängden på SSD:erna

Jag gav ju två förslag i ett, det första var att bara köra viktningen direkt i scriptet, det är definitivt enklast. Varför göra det krångligt för sig i onödan? :)


Alla tider är GMT +2. Klockan är nu 00:13.

Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson