Che cos'è un algoritmo leggibile per selezionare un acquirente casuale in base a ciò che hanno acquistato?

0

Sto lavorando su un algoritmo omaggio per selezionare un cliente in base a ciò che hanno acquistato:

  • Se acquistano l'articolo 1, ottengono 1 punto.
  • Se acquistano l'elemento 2, ottengono 2 punti.
  • Se acquistano l'elemento 3, ottengono 3 punti.

Ogni acquisto nel database ha un item_id , che posso utilizzare per capire quale oggetto hanno acquistato.

Sto cercando di fare il numero minimo di query al database per evitare che l'utilizzo della memoria salga (ad es. non riesco a recuperare tutti i record e calcolare i punti tutti in memoria), e sto cercando di mettere il minimo peso su MySQL. Ecco cosa mi è venuto in mente:

Passaggio 1: Ottieni punti totali

Faccio tre query count nel database, moltiplicando il conteggio per il moltiplicatore di punteggio per ciascun elemento, sommando i punteggi moltiplicati e inserendo i risultati in un array. Ad esempio, se ci sono 10 acquisti per l'articolo 1, 10 per l'articolo 2 e 10 per l'articolo 3, l'array results sarà simile a questo:

[
    1 => 10,
    2 => 30,
    3 => 60
];

Come probabilmente puoi capire, il primo elemento è solo se stesso (10), il secondo elemento è (10 * 2) + 10 ( 10 * 2 è il numero di acquisti moltiplicato per il moltiplicatore del punteggio e poi aggiunto all'elemento precedente) . Allo stesso modo, il terzo elemento dell'array è (10 * 3) + 30 .

Passaggio 2: selezionare un numero casuale

In questa fase, scelgo semplicemente un numero casuale compreso tra 1 e 60 (1 e il valore dell'ultimo elemento nell'array).

Passaggio 3: recupero del vincitore

In questa fase, eseguo quasi l'inverso del passaggio 1. Immagina che il numero casuale sia 33. Scopro quale valore è inferiore a questo numero casuale (è l'elemento nell'indice 2), quindi deduco il valore di quell'indice dal mio numero casuale ( 33 - 30 = 3 ), quindi divido il resto per il moltiplicatore del punteggio per index + 1 . In questo caso, il moltiplicatore di punteggio per 2 + 1 (numero articolo 3) è 3, quindi divido 3 per 3, che mi dà 1. Quindi recupererò il primo acquisto per l'articolo 3.

È complicato!

Mi ci è voluto un sacco di tempo solo per scrivere questo qui. Ora immagina qualcuno di fronte a un pezzo di codice che aggiunge e moltiplica e sottrae e divide sempre! C'è un modo per renderlo più semplice?

Non mi dispiace un po 'di perdita di prestazioni se mi guadagna una migliore leggibilità; Preferisco che il mio codice sia il più leggibile possibile, piuttosto che eseguire bene, ma è difficile da capire.

    
posta Parham Doustdar 11.10.2015 - 09:34
fonte

2 risposte

2

Devi utilizzare il totale parziale per eseguire la stessa somma che hai descritto, ma in SQL.

Supponendo che tu abbia tabelle come questa:

CREATE TABLE items(
  id int(10) auto_increment,
  score int(10),
  PRIMARY KEY(id)
);

CREATE TABLE purchases(
  id int(10) auto_increment,
  item_id int(10),
  PRIMARY KEY(id),
  FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
);

La query che stai cercando è qualcosa del tipo:

SELECT a.id, SUM(i.score) as running_total
FROM purchases a,
     purchases b JOIN items i ON i.id = b.item_id
WHERE b.id <= a.id
GROUP BY a.id
HAVING SUM(i.score) >= (SELECT ROUND(RAND() * (SELECT SUM(i.score) from purchases a JOIN items i ON i.id = a.item_id)))
ORDER BY a.id
LIMIT 1;

Creo il totale parziale unendo la tabella su se stesso, usando l'ID per l'ordine. b.id <= a.id assicura che il totale parziale acquisti solo gli acquisti precedenti.

Quindi applico un filtro SUM(i.score) >= (SELECT ROUND(RAND() * (SELECT SUM(i.score) from purchases a JOIN items i ON i.id = a.item_id))) e LIMIT 1 per ottenere un solo risultato.

Vedi SQLFiddle per la spiegazione passo passo.

L'unica preoccupazione è la performance, devi provarla sui tuoi dati reali. Se è male, credo che ci sia un modo per ottimizzare questo. Sostituire HAVING con un'altra condizione WHERE è probabilmente un buon modo per iniziare.

Articolo correlato: NoSQL? No, SQL! - Come calcolare totali parziali

    
risposta data 11.10.2015 - 15:19
fonte
1

Generalmente, la soluzione che ha RDBMS fa tutto per te di solito diventa la più leggera sul database, perché finisci per colpire il database solo una volta, invece di più volte. Questa è anche, per definizione, la soluzione che tasserebbe meno il web server, poiché porta solo al server i risultati finali, niente di più.

Quindi, devi fare il tutto usando una singola istruzione select, idealmente solo con join, ma con sottoselezioni annidate se necessario. Quindi, puoi provare a trovare modi più ottimali suddividendolo in pezzi, ma sarei disposto a scommettere che non troverai nulla che funzioni meglio.

( Modifica: non è corretto, vedi i commenti di scriptin e la risposta di scriptin.)

  • Seleziona i clienti.
  • Unisci a sinistra elementi acquistati.
  • Unisciti a sinistra con la tabella che elenca i punti per articolo.
  • Utilizza SUM() per calcolare i punti totali per cliente. Non ricordo i dettagli esatti, ma dovrai anche cercare il riferimento SQL per GROUP BY ed eventualmente anche per HAVING .
  • Utilizza ORDER BY con DESCENDING sulla somma dei punti per ottenere i primi clienti con il punteggio più alto.
  • Utilizza LIMIT 60 se vuoi selezionare qualcuno tra i primi 60.
  • Annidare quanto sopra in una nuova query che aggiunge una colonna contenente un numero casuale. (Vedi link )
  • Aggiungi ORDER BY alla colonna casuale.
  • prendi LIMIT 1 di quello se vuoi un solo vincitore.

Fai tutto quanto sopra in una singola query, annidando quanto necessario. Indenta bene, ma tieni a mente che, indipendentemente da quanto ti piaccia, alla fine sarà un brutto mostro. Tuttavia, SQL è un linguaggio relativamente semplice, quindi chiunque abbia una sufficiente esperienza con SQL dovrebbe essere in grado di capire cosa fa e come lo fa dopo aver dato abbastanza studio diligente. Uno dei principali vantaggi di fare tutto in una singola query SQL è che è tutto in un unico posto per qualcuno da leggere e capire. (Oltre al fatto che probabilmente sarà il modo migliore per ottenere un cliente casuale tra i migliori marcatori.)

Suggerimento: per testare questo, è necessario utilizzare la sintassi RAND(N) di MySQL, in modo da ottenere la stessa sequenza di numeri casuali ogni volta che si esegue la query. Se si desidera controllare anche i valori esatti dei numeri "casuali", creare una tabella separata con ID cliente e numeri casuali e LEFT JOIN quando si verifica l'algoritmo per assicurarsi che funzioni. Quindi, sostituiscilo con RAND() .

    
risposta data 11.10.2015 - 11:53
fonte

Leggi altre domande sui tag