2012-08-01 67 views
5

我想统计每个用户在彼此的'5'内有多少行。SQL:发现行之间的差异

例如,Don - 501和Don - 504应该计数,而Don - 501和Don - 1600不应该被计数。

开始:

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 

完成:

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

也许是我的眼睛,但你的数据似乎不符合文字描述.... _Don - 501和Don - 504应该被计数_但我没有看到这些值。 – Taryn 2012-08-01 16:20:30

+0

你能解释为什么莎拉的数不是3? 1110至1111是1,11111至1112是2,1110至1112是3,对吗?或者这不是你做这件事的方式? – mikeY 2012-08-01 16:29:04

回答

2

使用ABS()功能,结合自连接在一个子查询:

所以,像这样:

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 

根据Andreas的评论编辑。

+0

我认为这将包括a和b的所有排列,也就是说,对于“Don 6012”和“Don 6014”,将会有两行,其差异为2.这些必须以某种方式过滤出来;也许除以二? – Andreas 2012-08-01 16:27:19

+0

好点。编辑,谢谢! – 2012-08-01 16:34:05

+0

MySQL不喜欢WHERE子句中的'difference'。 (MySQL 5.5) – 2012-08-01 16:37:58

0

因为OP也希望得到零计数,所以我们需要一个自左连接。如果一个人有两个完全相同的值,则需要额外的逻辑,这些也应该只计算一次。

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 
     ; 

结果:

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

注:遗憾的CTE,我还没有看到日MySQL的标签,我只是喜欢这个问题;-)

+2

MySql支持CTE吗?我不认为它具有这种功能。 – Taryn 2012-08-01 17:00:09

+0

我只是注意到了我自己。我仍然喜欢这个解决方案,但是...(问题或多或少都是一般的) – wildplasser 2012-08-01 17:01:44

+0

使用窗口函数会更容易(想到'lag()') – 2012-08-01 17:04:08

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

假设每个name - >value对是唯一的,这会得到你的计数值在5以内每个名字

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 

正如你会注意到的,我们也必须排除等于他们自己的配对。

但是,如果你想算的次数每个名字的值在表任何值的范围内来了,你可以使用:

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 

SQLFiddle Demo两个解决方案。