2010-03-12 26 views
39

Sono in cerca di aiuto utilizzando sum() nella mia query SQL:sum MYSQL() per le righe distinte

SELECT links.id, 
     count(DISTINCT stats.id) as clicks, 
     count(DISTINCT conversions.id) as conversions, 
     sum(conversions.value) as conversion_value 
FROM links 
LEFT OUTER JOIN stats ON links.id = stats.parent_id 
LEFT OUTER JOIN conversions ON links.id = conversions.link_id 
GROUP BY links.id 
ORDER BY links.created desc; 

Io uso DISTINCT perché sto facendo "gruppo da" e questo assicura la stessa riga non viene contato più di una volta.

Il problema è che SUM (conversions.value) conta il "valore" per ogni riga più di una volta (a causa del gruppo da)

Io fondamentalmente voglio fare SUM(conversions.value) per ogni conversions.id DISTINCT.

È possibile?

+1

L'invio dell'interrogazione completa sarebbe utile. Come si duplicano i valori con un 'GROUP BY'? – Matthew

+0

Stai partecipando? Dovresti pubblicare la tua richiesta. Esistono alcune opzioni che è possibile utilizzare in base alla query. –

+0

Ho aggiornato la mia domanda con la domanda completa – makeee

risposta

58

Posso sbagliarmi, ma da quanto ho capito

  • conversions.id è il primari chiave della tabella conversioni
  • stats.id è la chiave primaria della tabella statistiche

Così per ogni conversions.id si hanno al massimo una links.id influenzato.

richiesta Si è un po 'come fare il prodotto cartesiano di 2 set:

[clicks] 
SELECT * 
FROM links 
LEFT OUTER JOIN stats ON links.id = stats.parent_id 

[conversions] 
SELECT * 
FROM links 
LEFT OUTER JOIN conversions ON links.id = conversions.link_id 

e per ogni link, si ottiene sizeof ([click]) x sizeof ([conversioni]) linee

come annotato il numero di conversioni uniche nella richiesta può essere ottenuto tramite un

count(distinct conversions.id) = sizeof([conversions]) 

questo riesce distinta per rimuovere tutti i [click] linee nel prodotto cartesiano

ma chiaramente

sum(conversions.value) = sum([conversions].value) * sizeof([clicks]) 

Nel tuo caso, dal momento che

count(*) = sizeof([clicks]) x sizeof([conversions]) 
count(*) = sizeof([clicks]) x count(distinct conversions.id) 

avete

sizeof([clicks]) = count(*)/count(distinct conversions.id) 

così ho iniziato ad esaminare la tua richiesta con

SELECT links.id, 
    count(DISTINCT stats.id) as clicks, 
    count(DISTINCT conversions.id) as conversions, 
    sum(conversions.value)*count(DISTINCT conversions.id)/count(*) as conversion_value 
FROM links 
LEFT OUTER JOIN stats ON links.id = stats.parent_id 
LEFT OUTER JOIN conversions ON links.id = conversions.link_id 
GROUP BY links.id 
ORDER BY links.created desc; 

Tienimi aggiornato! Jerome

+5

Sei un genio! Ho quasi pensato che non ci fosse una soluzione fino a quando non ho trovato la tua risposta. –

+1

Grande, questa soluzione è perfetta e abbastanza universale quando non si vuole gestire una soluzione di subquery dipendente che non è accettabile per grandi serie di dati. –

+0

La soluzione Jeromes è in realtà sbagliata e può produrre risultati errati !! Vedi la mia risposta qui sotto. –

3

Io uso una sottoquery per fare ciò. Elimina i problemi con il raggruppamento. Quindi la query sarebbe qualcosa di simile:

SELECT COUNT(DISTINCT conversions.id) 
... 
    (SELECT SUM(conversions.value) FROM ....) AS Vals 
+0

Domanda aggiornata con la mia domanda completa. Non sono sicuro di come integrare una subquery in quello che ho e in che modo influirebbe sulle prestazioni. – makeee

+0

Le subquery normalmente influiscono negativamente sulle prestazioni. Per ridurre al minimo l'impatto, assicurati che qualsiasi subquery agisca su un indice. – Dave

4

utilizzare la seguente query:

SELECT links.id 
    , (
    SELECT COUNT(*) 
    FROM stats 
    WHERE links.id = stats.parent_id 
) AS clicks 
    , conversions.conversions 
    , conversions.conversion_value 
FROM links 
LEFT JOIN (
    SELECT link_id 
    , COUNT(id) AS conversions 
    , SUM(conversions.value) AS conversion_value 
    FROM conversions 
    GROUP BY link_id 
) AS conversions ON links.id = conversions.link_id 
ORDER BY links.created DESC 
2

Che ne dite di qualcosa di simile:

select l.id, count(s.id) clicks, count(c.id) clicks, sum(c.value) conversion_value 
from (SELECT l.id id, l.created created, 
       s.id clicks, 
       c.id conversions, 
       max(c.value) conversion_value      
     FROM links l LEFT 
     JOIN stats s ON l.id = s.parent_id LEFT 
     JOIN conversions c ON l.id = c.link_id 
     GROUP BY l.id, l.created, s.id, c.id) t 
order by t.created 
6

Per una spiegazione del motivo per cui si stava vedendo i numeri errati, read this.

Penso che Jerome abbia una presa in carico su ciò che sta causando il tuo errore. La query di Bryson funzionerebbe, sebbene avere quella sottoquery in SELECT potrebbe essere inefficiente.

+0

+1 per fornire un buon riferimento sull'utilizzo delle sottoquery. – kta

5

La soluzione di jomeomi è in realtà sbagliata e può produrre risultati errati !!

sum(conversions.value)*count(DISTINCT conversions.id)/count(*) as conversion_value 

supponiamo seguente tabella

conversions 
id value 
1 5 
1 5 
1 5 
2 2 
3 1 

la corretta somma di valore per ids distinti sarebbe 8. formula di Jerome produce:

sum(conversions.value) = 18 
count(distinct conversions.id) = 3 
count(*) = 5 
18*3/5 = 9.6 != 8 
+0

e la risposta corretta è ..? – kleopatra

+1

Supponendo che le conversioni.id è un campo univoco, non è possibile che un JOIN possa produrre 3 righe con conversions.id = 1 e solo 1 riga dove conversions.id = 2. Il presupposto che conversions.id sia univoco è implicito e dovrebbe probabilmente essere fatto esplicito, ma a parte questo, la formula è solida. – Jonathan

1

Ciò farà il trucco, basta dividi la somma con il conteggio dell'id di conversazione che sono duplicati.

SELECT a.id, 
     a.clicks, 
     SUM(a.conversion_value/a.conversions) AS conversion_value, 
     a.conversions 
FROM (SELECT links.id, 
     COUNT(DISTINCT stats.id) AS clicks, 
     COUNT(conversions.id) AS conversions, 
     SUM(conversions.value) AS conversion_value 
     FROM links 
     LEFT OUTER JOIN stats ON links.id = stats.parent_id 
     LEFT OUTER JOIN conversions ON links.id = conversions.link_id 
     GROUP BY conversions.id,links.id 
     ORDER BY links.created DESC) AS a 
GROUP BY a.id