2012-08-01 17 views
5

Voglio contare quante volte ogni utente ha file all'interno di "5" l'una dall'altra.SQL: trovare le differenze tra le righe

Ad esempio, Don - 501 e Don - 504 devono essere contati, mentre Don - 501 e Don - 1600 non devono essere contati.

Inizio:

Name  value 
_________ ______________ 
Don   1235 
Don   6012 
Don   6014 
Don   6300 
James  9000 
James  9502 
James  9600 
Sarah  1110 
Sarah  1111 
Sarah  1112 
Sarah  1500 
Becca  0500 
Becca  0508 
Becca  0709 

Finitura:

Name   difference_5 
__________  _____________ 
Don    1 
James   0 
Sarah   2 
Becca   0 
+3

forse sono i miei occhi ma i tuoi dati non sembrano corrispondere alla descrizione del testo .... _Don - 501 e Don - 504 dovrebbero essere contati_ ma non vedo questi valori. – Taryn

+0

Puoi spiegare perché il conteggio di Sarah non è 3? 1110 a 1111 è 1, 1111 a 1112 è 2, 1110 a 1112 è 3, giusto? O non è così che lo stai facendo? – mikeY

risposta

2

utilizzare la funzione ABS(), in combinazione con un self-join in una sottoquery:

Quindi, qualcosa di simile:

SELECT name, COUNT(*)/2 AS difference_5 
FROM (
    SELECT a.name name, ABS(a.value - b.value) 
    FROM tbl a JOIN tbl b USING(name) 
    WHERE ABS(a.value - b.value) BETWEEN 1 AND 5 
) AS t GROUP BY name 

modificato come da commento di Andreas.

+0

Penso che questo includa tutte le permutazioni di aeb, cioè, per "Don 6012" e "Don 6014" ci sarebbero due righe con la differenza 2. Queste dovrebbero essere filtrate in qualche modo; forse dividendo per due? – Andreas

+0

Buon punto. Modificato, grazie! –

+0

A MySQL non piace la "differenza" nella clausola WHERE. (MySQL 5.5) –

0

Poiché l'OP vuole anche contare sullo zero, avremo bisogno di un join self-left. È necessaria una logica aggiuntiva se una persona ha due esattamente gli stessi valori, anche questi dovrebbero essere contati una sola volta.

WITH cnts AS (
     WITH pair AS (
       SELECT t1.zname,t1.zvalue 
       FROM ztable t1 
       JOIN ztable t2 
       ON t1.zname = t2.zname 
       WHERE (t1.zvalue < t2.zvalue 
         AND t1.zvalue >= t2.zvalue - 5) 
       OR (t1.zvalue = t2.zvalue AND t1.ctid < t2.ctid) 
       ) 
     SELECT DISTINCT zname 
     , COUNT(*) AS znumber 
     FROM pair 
     GROUP BY zname 
     ) 
, names AS (
     SELECT distinct zname AS zname 
     FROM ztable 
     GROUP BY zname 
     ) 
SELECT n.zname 
     , COALESCE(c.znumber,0) AS znumber 
FROM names n 
LEFT JOIN cnts c ON n.zname = c.zname 
     ; 

RISULTATO:

DROP SCHEMA 
CREATE SCHEMA 
SET 
CREATE TABLE 
INSERT 0 14 
zname | znumber 
-------+--------- 
Sarah |  3 
Don |  1 
Becca |  0 
James |  0 
(4 rows) 

NOTA: Ci scusiamo per il CTE, che non aveva visto esimo tag mysql, mi piaceva il problema ;-)

+2

MySql supporta CTE? Non pensavo che avesse quella funzionalità. – Taryn

+0

L'ho notato solo io. Mi piace ancora la soluzione, anche se ... (e il problema è più o meno generale) – wildplasser

+0

Sarebbe ancora più semplice con le funzioni di finestra (mi viene in mente 'lag()') –

0
SELECT 
    A.Name, 
    SUM(CASE WHEN (A.Value < B.Value) AND (A.Value >= B.Value - 5) THEN 1 ELSE 0 END) Difference_5 
FROM 
    tbl A INNER JOIN 
    tbl B USING(Name) 
GROUP BY 
    A.Name 
1

Supponendo che ogni name ->value pair è univoco, questo ti farà contare il numero di volte che il valore è compreso tra 5 per nome:

SELECT a.name, 
      COUNT(b.name)/2 AS difference_5 
FROM  tbl a 
LEFT JOIN tbl b ON a.name = b.name AND 
        a.value <> b.value AND 
        ABS(a.value - b.value) <= 5 
GROUP BY a.name 

Come noterete, abbiamo anche escludere le coppie che sono uguali a se stessi.

Ma se si voleva contare il numero di volte in cui i valori di ogni nome è venuto entro 5 di qualsiasi valore nella tabella, è possibile utilizzare:

SELECT a.name, 
      COUNT(b.name)/2 AS difference_5 
FROM  tbl a 
LEFT JOIN tbl b ON NOT (a.name = b.name AND a.value = b.value) AND 
        ABS(a.value - b.value) <= 5 
GROUP BY a.name 

Vedere la SQLFiddle Demo per entrambi soluzioni.

Problemi correlati