2012-08-01 11 views
5

Chcę policzyć, ile razy każdy użytkownik ma wiersze w obrębie "5" siebie nawzajem.SQL: znajdowanie różnic między wierszami

Na przykład należy liczyć Don-501 i Don-504, natomiast Don - 501 i Don - 1600 nie należy liczyć.

Start:

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 

Wykończenie:

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

Może to moje oczy, ale twoje dane nie pasują do opisu tekstu ... _Don - 501 i Don - 504 powinny być policzone, ale nie widzę tych wartości. – Taryn

+0

Czy możesz wyjaśnić, dlaczego liczba Sary nie wynosi 3? 1110 do 1111 to 1, 1111 do 1112 to 2, 1110 do 1112 to 3, prawda? Czy to nie jest sposób w jaki to robisz? – mikeY

Odpowiedz

2

Użyj funkcji ABS(), w połączeniu z samosprzężenie w podkwerendzie:

Tak, coś takiego:

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 

zredagowany zgodnie z komentarzem Andreasa.

+0

Myślę, że będzie to zawierało wszystkie permutacje a i b, to znaczy, dla "Don 6012" i "Don 6014" byłyby dwa wiersze z różnicą 2. Trzeba by je jakoś odfiltrować; może dzielisz przez dwa? – Andreas

+0

Dobra uwaga. Edytowane, dzięki! –

+0

MySQL nie lubi "różnicy" w klauzuli WHERE. (MySQL 5.5) –

0

Ponieważ PO również chce zliczeń zerowych, potrzebujemy samo-lewicowego łączenia. Dodatkowa logika jest potrzebna, jeśli jedna osoba ma dwie dokładnie takie same wartości, te powinny być liczone tylko raz.

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 
     ; 

WYNIK:

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

UWAGA: przepraszam za CTE, nie widziałem th tagu mysql, po prostu lubił problem ;-)

+2

Czy MySql obsługuje CTE? Nie sądziłem, że ma tę funkcjonalność. – Taryn

+0

Właśnie to zauważyłem. Nadal jednak podoba mi się to rozwiązanie ... (a problem jest mniej lub bardziej ogólny) – wildplasser

+0

Byłoby jeszcze łatwiej z funkcjami okienkowymi (przychodzi mi na myśl '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

Zakładając, że każdy name ->value Para jest wyjątkowa, dzięki temu uzyskasz liczbę razy wartość od 5 na imię:

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 

Jak zauważysz, musimy również wykluczyć pary, które są sobie równe.

Ale jeśli chce liczyć ile razy wartości każdej nazwy przyszedł w ciągu 5 dowolnej wartości w tabeli, można użyć:

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 

Zobacz SQLFiddle Demo dla obu rozwiązania.