2012-08-01 16 views
5

Quiero contar cuántas veces cada usuario tiene filas dentro de '5' entre sí.SQL: encontrar diferencias entre las filas

Por ejemplo, Don - 501 y Don - 504 se deben contar, mientras que Don - 501 y Don - 1600 no se deben contar.

de inicio:

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 

Acabado:

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

tal vez sean mis ojos, pero sus datos no parecen coincidir con la descripción del texto ... _Don - 501 y Don - 504 deben contarse_ pero no veo estos valores. – Taryn

+0

¿Podría explicar por qué el conteo de Sarah no es 3? 1110 a 1111 es 1, 1111 a 1112 es 2, 1110 a 1112 es 3, ¿verdad? ¿O no es así como lo haces? – mikeY

Respuesta

2

utilizar la función ABS(), en conjunción con una autocombinación en una subconsulta:

Por lo tanto, algo así como:

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 

editado según el comentario de Andreas.

+0

Creo que esto incluirá todas las permutaciones de ayb, es decir, para "Don 6012" y "Don 6014" habría dos filas con diferencia 2. Estas deberían filtrarse de alguna manera; tal vez dividir por dos? – Andreas

+0

Buen punto. Editado, gracias! –

+0

MySQL no le gusta la 'diferencia' en la cláusula WHERE. (MySQL 5.5) –

0

Debido a que el OP también quiere de cero recuentos, necesitaremos uniones a la izquierda. Se necesita lógica adicional si una persona tiene dos exactamente los mismos valores, estos también se deben contar una sola vez.

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 
     ; 

RESULTADO:

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

NOTA: Lo siento por el CTE, que no había visto º etiqueta de MySQL, simplemente me gustaba el problema ;-)

+2

¿MySql es compatible con CTE? No pensé que tuviera esa funcionalidad. – Taryn

+0

Me acabo de dar cuenta de eso. Aunque me gusta la solución, sin embargo ... (y el problema es más o menos general) – wildplasser

+0

Sería aún más fácil con las funciones de ventana (me viene a la '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

Suponiendo que cada name ->value par es único, esto le dará la cuenta de veces que el valor está dentro de 5 por nombre:

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 

Como se dará cuenta, también hay que excluir a las parejas que son iguales a sí mismos.

Pero si desea contar el número de veces que los valores de cada nombre vino dentro de 5 de cualquier valor en la tabla, puede utilizar:

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 

Véase el SQLFiddle Demo para ambos soluciones.

Cuestiones relacionadas