2009-12-17 82 views
6

在MySQL选择底部2,我想从每个类别SQL查询,以便从每个类别

Category Value 
1  1.3 
1  4.8 
1  3.7 
1  1.6 
2  9.5 
2  9.9 
2  9.2 
2  10.3 
3  4 
3  8 
3  16 

选择给我下的2项:

Category Value 
1  1.3 
1  1.6 
2  9.5 
2  9.2 
3  4 
3  8 

之前,我从sqlite3的我不得不迁移首先从每个类别中选择一个最低值,然后排除任何加入的值,我不得不再次从每个类别中选择最低值。然后任何等于新类别中最低或最低的东西都会赢。这也会选择超过2的情况下,这是一个烦人的...它也有一个非常长的运行时间。

我的最终目标是计算一个人处于某个类别中最低2个(也有一个姓名字段)的次数,这是我不知道该怎么做的一部分。 感谢

+0

是否有还有一个ID这对每行独特之处? – 2009-12-17 18:37:08

+1

既然你不喜欢这些关系,你打算如何避免这些关系?任何人提出的任何解决方案都必须以某种方式处理关系,因此您应该尝试具体说明管理它们的规则。 – 2009-12-17 19:22:11

+2

我用'greatest-n-per-group'标记了这个问题,因为它与使用该标签在StackOverflow上询问的许多其他问题类似。虽然我知道你要求每组的最小值*,但解决这个问题的方法是一样的。 – 2009-12-17 19:24:51

回答

4

你可以试试这个:

SELECT * FROM (
    SELECT c.*, 
     (SELECT COUNT(*) 
     FROM user_category c2 
     WHERE c2.category = c.category 
     AND c2.value < c.value) cnt 
    FROM user_category c) uc 
WHERE cnt < 2 

它应该给你想要的结果,但检查表现还算可以。

+0

这不起作用。它为类别2返回9.2和10.3。 – 2009-12-17 19:01:17

+0

很抱歉听到。我试过了,它适用于我。你能否检查一下你的测试数据是否正确?谢谢! – 2009-12-17 19:06:45

+0

是的,我完全按照上面所示的顺序和全部顺序输入。类别1的值恢复正确,(1.3和1.6),但类别2错误,并且也返回2(返回4和16)。此外,这个查询甚至不会执行,直到您给第一个子选择别名。 – 2009-12-17 19:12:56

1

工会应该工作。与Peter的解决方案相比,我不确定其性能。

SELECT smallest.category, MIN(smallest.value) 
    FROM categories smallest 
GROUP BY smallest.category 
UNION 
SELECT second_smallest.category, MIN(second_smallest.value) 
    FROM categories second_smallest 
    WHERE second_smallest.value > (SELECT MIN(smallest.value) FROM categories smallest WHERE second.category = second_smallest.category) 
GROUP BY second_smallest.category 
+0

在子选择的where子句中存在拼写错误,应该是“WHERE smallest.category = second_smallest.category”。 – 2009-12-17 19:02:00

+1

另外,如果给定类别中的最小值有联系,则这不会给出正确的结果。 – 2009-12-17 19:03:03

+0

要消除联系,只需添加DISTINCT? – 2009-12-17 19:12:50

8
SELECT c1.category, c1.value 
FROM catvals c1 
LEFT OUTER JOIN catvals c2 
    ON (c1.category = c2.category AND c1.value > c2.value) 
GROUP BY c1.category, c1.value 
HAVING COUNT(*) < 2; 

测试在MySQL 5.1.41与您的测试数据。输出:

+----------+-------+ 
| category | value | 
+----------+-------+ 
|  1 | 1.30 | 
|  1 | 1.60 | 
|  2 | 9.20 | 
|  2 | 9.50 | 
|  3 | 4.00 | 
|  3 | 8.00 | 
+----------+-------+ 

(额外的小数位,因为我宣布valueNUMERIC(9,2)

像其他的解决方案,这一点,如果有关系产生每个类别超过2行。有很多方法可以构建联接条件来解决这个问题,但我们需要在表中使用主键或唯一键,并且我们还必须知道如何解决您的联系。

+0

这太棒了!正是我期待的!谢谢! – 2010-09-16 18:29:51

1

这是一个非常普遍的解决方案,它可以为每个类别选择前n行。即使有重复值,这也可以工作。

/* creating temporary variables */ 
mysql> set @cnt = 0; 
mysql> set @trk = 0; 

/* query */ 
mysql> select Category, Value 
     from (select *, 
       @cnt:=if(@trk = Category, @cnt+1, 0) cnt, 
       @trk:=Category 
       from user_categories 
       order by Category, Value) c1 
     where c1.cnt < 2; 

这是结果。

+----------+-------+ 
| Category | Value | 
+----------+-------+ 
|  1 | 1.3 | 
|  1 | 1.6 | 
|  2 | 9.2 | 
|  2 | 9.5 | 
|  3 |  4 | 
|  3 |  8 | 
+----------+-------+ 

这是在MySQL测试5.0.88 注意@trk变量的初始值应该是不类别字段的最低值。

1

这是一个能正确处理重复的解决方案。表名是“ZZZ”和列是int和float

select 
    smallest.category category, min(smallest.value) value 
from 
    zzz smallest 
group by smallest.category 

union 

select 
    second_smallest.category category, min(second_smallest.value) value 
from 
    zzz second_smallest 
where 
    concat(second_smallest.category,'x',second_smallest.value) 
    not in (-- recreate the results from the first half of the union 
     select concat(c.category,'x',min(c.value)) 
     from zzz c 
     group by c.category 
    ) 
group by second_smallest.category 

order by category 

注意事项:

  • 如果只有一个给定类别的值,则返回只有一个条目。
  • 如果每行有一个唯一的recordID,则不需要所有连接来模拟唯一键。

您的里程可能会有所不同,

--mark