2013-04-30 5 views
11

sto trovando questa abbastanza difficile da spiegare quindi per favore nudo con me qui ...Come controllare contro tutti i join quando si genera un punteggio utilizzando MYSQL

Sto usando MySQL per generare un punteggio per ogni risultato restituito da una domanda. I risultati vengono quindi ordinati in base al punteggio.

La parte che non sembra funzionare correttamente è quando sto cercando di aggiungere un punteggio per ogni tag che è stato cercato e il risultato è assegnato a. Quindi diciamo che faccio una ricerca per i tag "esempio", "test" e "tag" e uno dei miei risultati è assegnato ai tag "esempio", "test", "someothertag" dovrebbe venire con un punteggio di 10 dal momento che ci sono 2 partite.

che cosa realmente sta accadendo è che sto ottenendo un punteggio di 5 se v'è una corrispondenza, indipendentemente dal numero di tag sono abbinati. e 0 se nessun tag sono abbinati.

Qui è un esempio di una delle query che viene generato da una ricerca.

 SELECT DISTINCT results.*, 
        ( 
         5*(MATCH(tags.name) AGAINST('"self employed"' IN BOOLEAN MODE)) + 
      5*(MATCH(tags.name) AGAINST('"rental income"' IN BOOLEAN MODE)) + 
      5*(MATCH(tags.name) AGAINST('"commission income"' IN BOOLEAN MODE)) + 
      5*(MATCH(tags.name) AGAINST('"bankruptcy"' IN BOOLEAN MODE)) + 
      5*(MATCH(tags.name) AGAINST('"condo approval"' IN BOOLEAN MODE)) + 

         1*usefulness + 
         10*shares 
        ) AS score 
     FROM results 
     INNER JOIN categories c on results.ID = c.RESULT_ID 
     INNER JOIN tags ON results.id = tags.result_id 
     WHERE c.name in ('purchase', 'condo', 'va') 
     AND (tags.name = 'self employed' OR tags.name = 'rental income' OR tags.name = 'commission income' OR tags.name = 'bankruptcy' OR tags.name = 'condo approval' ) 
     AND (results.scope = 'all' OR results.scope = 'hi') 
     AND published = 1 

     GROUP BY results.ID 
     having count(distinct c.c_id) = 3 
     ORDER BY score DESC 
     LIMIT 8 OFFSET 0 
+1

Probabilmente non è necessario utilizzare un indice di testo completo per controllare i nomi dei tag in questa query. Sarebbe più veloce e più conciso fare solo un confronto diretto sull'eguaglianza. –

+0

Sarà probabilmente più chiaro per le persone se è possibile scrivere lo schema e alcuni dati di esempio per le tabelle e un elenco di ciò che si desidera che la query generi per i dati di esempio. – Mikeb

+0

Il raggruppamento potrebbe provocare perdite di partite. Hai provato ad aggiungere COUNT ai tuoi cinque 5 * (MATCH (tags.name) ... articoli? – nicolas

risposta

0

è necessario sum() il punteggio perché una linea corrisponde un solo tag.

Nella query ha selezionato più righe e le ha raggruppate in base all'ID, quindi si ottiene solo il risultato per UNA riga e che sarà sempre 5 nel tuo caso.

1

Come consigliato da Sam Dufel, probabilmente non è necessaria la ricerca di testo completo, soprattutto poiché si utilizza il confronto di stringhe esatte nelle clausole WHERE.

Inoltre, a causa dei molti-a-molti tra results e categories (assunto dalla clausola HAVING COUNT(c_id) = 3), credo che in nessun modo è possibile partecipare sia categories e tags nella stessa query.

Senza la clausola GROUP BY, per un dato result, si otterrebbe una riga per ogni corrispondenza category. Per ogni coppia corrispondente (result, category), otterresti una riga per ogni corrispondenza tag.name corrispondente. Non penso che ci sia un modo per affrontare un simile risultato.

Quello che vorrei suggerire è:

Fase 1: ottenere results presente in tutte e tre le categorie

SELECT results.ID 
FROM results 
JOIN categories ON results.id = categories.result_id 
WHERE categories.name IN ('purchase', 'condo', 'va') 
GROUP BY results.ID 
HAVING COUNT(DISTINCT c.c_id) = 3 

Fase 2: calcolo punteggio di qualsiasi results corrispondenza almeno una stringa di ricerca

SELECT 
    DISTINCT results.*, -- DISTINCT is redundant because of the GROUP BY clause 
    ( 
     5*(COUNT(tags.result_id)) + -- you actually want to count the number of matches! 
     1*usefulness + -- warning, see below 
     10*shares  -- warning, see below 
    ) AS score 
FROM results 
INNER JOIN tags ON results.id = tags.result_id 
WHERE 
    tags.name = 'self employed' 
    OR tags.name = 'rental income' 
    OR tags.name = 'commission income' 
    OR tags.name = 'bankruptcy' 
    OR tags.name = 'condo approval' 
GROUP BY results.ID 

Fase 3: mettere tutto insieme

SELECT 
    results.*, 
    ( 
     5*(COUNT(tags.result_id)) + 
     1*usefulness + -- warning, see below 
     10*shares  -- warning, see below 
    ) AS score 
FROM (
     SELECT results.id 
     FROM results 
     JOIN categories ON results.id = categories.result_id 
     WHERE 
      categories.name IN ('purchase', 'condo', 'va') 
      AND (results.scope = 'all' OR results.scope = 'hi') 
      AND published = 1 
     GROUP BY results.id 
     HAVING COUNT(DISTINCT categories.c_id) = 3 
) AS results_subset 
JOIN results ON results_subset.id = results.id 
JOIN tags ON results.id = tags.result_id 
WHERE 
    tags.name = 'self employed' 
    OR tags.name = 'rental income' 
    OR tags.name = 'commission income' 
    OR tags.name = 'bankruptcy' 
    OR tags.name = 'condo approval' 
GROUP BY results.ID 

Avviso dove ho scelto di includere le condizioni in cui il scope e published. Questa scelta si basa sul principio che i filtri dovrebbero essere dichiarati il ​​prima possibile. È possibile ottenere prestazioni migliori se le si inserisce nella query esterna, ma in realtà dipende dalle cardinalità.

Avvertenza: campi usefulness e shares non fanno parte della funzione GROUP BY non inclusa in una funzione di aggregazione. Questo è allowed by MySQL ma estremamente pericoloso. Se usefulness e shares appartengono a una tabella diversa da result (la tabella è in GROUP'ed BY), i valori restituiti nella query non sono definiti.

+0

Grazie mille per la risposta dettagliata e mi dispiace che mi ci sia voluto così tanto tempo per rispondere. l'ultimo paio di giorni. Darò questo stasera più tardi e ti farò sapere come andrò avanti! –

1

scrivere come segue:

"sum((5*(MATCH(tags.name) AGAINST('"self employed"' IN BOOLEAN MODE))), 
     (5*(MATCH(tags.name) AGAINST('"rental income"' IN BOOLEAN MODE))) , 
     (5*(MATCH(tags.name) AGAINST('"commission income"' IN BOOLEAN MODE))), 
     (5*(MATCH(tags.name) AGAINST('"bankruptcy"' IN BOOLEAN MODE))), 
     (5*(MATCH(tags.name) AGAINST('"condo approval"' IN BOOLEAN MODE))), 
     (1*usefulness), (10*shares)) as score" 
0

penso che la query è troppo complicato. Prova questo:

SELECT 
    results.*, 
    5 * count(distinct tags.name) + 1*usefulness + 10*shares AS score 
FROM results 
JOIN categories c on results.ID = c.RESULT_ID 
    AND c.name in ('purchase', 'condo', 'va') 
JOIN tags ON results.id = tags.result_id 
    AND tags.name in ('self employed', 'rental income', 'commission income', 'bankruptcy', 'condo approval') 
WHERE results.scope in ('all', 'hi') 
AND published = 1 
GROUP BY 1, 2, 3, 4, 5 -- list as many numbers here as there are columns in "results" 
HAVING count(distinct c.c_id) = 3 
ORDER BY score DESC 
LIMIT 8 OFFSET 0 

Un problema chiave che si aveva era il raggruppamento - per farlo funzionare correttamente, è necessario o nome, o un riferimento da posizione selezionata, tutti colonne della tabella results. Non hai dato lo schema della tabella, quindi non potevo sapere cosa scrivere. Ho indovinato 5 colonne, da cui il GROUP BY 1, 2, 3, 4, 5, ma devi assicurarti che sia corretto.

Ho riordinato i tuoi OR s modificandoli a IN s - facendo ciò consentiremo l'uso degli indici su queste colonne, se tali indici esistono ("OR" non userà un indice).

Ho spostato alcune delle condizioni della clausola WHERE nelle condizioni JOIN in cui aveva senso - questo dovrebbe migliorare le prestazioni.

Problemi correlati