我有两张桌子:
Types Data
+----+----------+ +-------+-------+
| id | name | | id | type |
+----+----------+ +-------+-------+
| 1 | name1 | | 1 | 1 |
| 2 | name2 | | 2 | 5 |
| 3 | name3 | | 3 | 7 |
| 4 | name4 | | 4 | 4 |
| 5 | name5 | | 5 | 2 |
| 6 | name6 | | 6 | 6 |
| 7 | name7 | | 7 | 3 |
| .. | .. | | 8 | 5 |
+----+----------+ | 9 | 5 |
| 10 | 4 |
| 11 | 1 |
| 12 | 2 |
| 13 | 6 |
| 14 | 5 |
| 15 | 2 |
| ... | ... |
| 1...? | 1...? |
+-------+-------+
数据表非常大,它包含我需要选择1000行的数百万行,但结果必须来自整个表,所以每第n行选择一次。我使用How to select every nth row in mySQL starting at n的答案完成了这个,但是,我需要为它添加一些逻辑,我需要一个选择查询来选择所有类型的每一行。我想这听起来很复杂,所以我会试着描述一下我想要实现的目标:
假设有7种类型和数据表有7M行0.5M行用于类型1,2,3,1.5M行用于类型4,5,6,7(只是清晰的间隔现在可能等于所有的类型)。
我需要1000个包含相同数量类型的记录,所以如果我有7种类型,每种类型都可以出现在结果集ROUND(1000/7)中,这将等于每种类型142个记录,所以我需要从Data中选择142个类型表;
对于包含0.5M行的类型1,2,3,其将是ROUND(0.5M / 142),等于每个第3521行; 对于类型4,5,6,7,其中包含1.5M行,这些行将是ROUND(1.5M / 142),等于每个第10563行;
所以结果看起来像这样:
Result
+-------+------+
| id | type |
+-------+------+
| 1 | 1 |
| 3522 | 1 |
| 7043 | 1 |
| .. | .. |
| .. | 2 |
| .. | 2 |
| .. | .. |
| .. | 3 |
| .. | 3 |
| .. | .. |
| .. | 4 |
| .. | 4 |
| .. | .. |
| .. | 5 |
| .. | 5 |
| .. | .. |
| .. | 6 |
| .. | 6 |
| .. | .. |
| .. | 7 |
| .. | 7 |
| .. | .. |
+-------+------+
我可以简单地在任何编程语言中使用多个查询从Data表中返回每个类型的计数,然后在进行数学选择时只选择单个类型。
但我想纯粹在MySQL中这样做,使用尽可能少的查询。
我会尝试更详细地解释一下我用实例来实现的目标。
我有1437823行的表。表模式如下所示:
+---------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| type | int(11) | NO | | NULL | |
| counter | int(11) | NO | | NULL | |
| time | datetime | NO | | NULL | |
+---------+----------+------+-----+---------+----------------+
该表类型统计信息为:
+------+-----------+
| Type | Row Count |
+------+-----------+
| 1 | 135160 |
| 2 | 291416 |
| 3 | 149863 |
| 4 | 296293 |
| 5 | 273459 |
| 6 | 275929 |
| 7 | 15703 |
+------+-----------+
(P.S。类型计数可以及时改变。)
假设我需要从时间间隔中选择样本数据,在问题的第一个版本中,我省略了时间,因为我认为它是无关紧要的,但现在我认为在订购提高性能时它可能有一些意义。
所以无论如何我需要选择大约1000行样本,其中每种类型都有相同的数据块,因此最终结果的统计结果如下所示: 我选择了7行类型的1000行,所以ROUND(1000/7)=每行143行;
+------+-----------+
| Type | Row Count |
+------+-----------+
| 1 | 143 |
| 2 | 143 |
| 3 | 143 |
| 4 | 143 |
| 5 | 143 |
| 6 | 143 |
| 7 | 143 |
+------+-----------+
所以现在我需要在时间间隔内的相等间隙中为每种类型选择143行。所以对于单一类型,它看起来像这样:
SET @start_date := '2014-04-06 22:20:21';
SET @end_date := '2015-02-20 16:20:58';
SET @nth := ROUND(
(SELECT COUNT(*) FROM data WHERE type = 1 AND time BETWEEN @start_date AND @end_date) / ROUND(1000 / (SELECT COUNT(*) FROM types))
);
SELECT r.*
FROM (SELECT * FROM data WHERE type = 1 AND time BETWEEN @start_date AND @end_date) r
CROSS
JOIN ( SELECT @i := 0 ) s
HAVING ( @i := @i + 1) MOD @nth = 1
统计:
+------+-----------+
| Type | Row Count |
+------+-----------+
| 1 | 144 |
+------+-----------+
这个查询会给我带来可接受性能的所需结果,但是我需要对每种类型进行查询,这会降低性能并且稍后需要将结果连接成单个数据集,因为这是我进一步处理所需的,所以我会喜欢在单个查询中执行此操作或至少获取单个结果集。
P.S。只要类型块相等,我就可以容忍结果集中的行计数偏差。
答案 0 :(得分:0)
你想要的是一个分层的样本。获得分层样本的一个好方法是按类型对行进行排序并分配一个序号 - 编号不必为每种类型重新开始。
然后,您可以通过获取每个第n个值获得1000行:
select d.*
from (select d.*, (@rn := @rn + 1) as rn
from data d cross join
(select @rn := 0) vars
order by type
) d
where mod(rn, floor( @rn / 1000 )) = 1;
注意:最后的比较是n行中的1行接近1000.根据值的数量,它可能会被一两个关闭。
编辑:
哎呀,上面做了一个分层样本,它与数据中类型的原始分布相匹配。要获得每个组的相同计数,请随机枚举它们并为每个组选择第一个“n”:
select d.*
from (select d.*,
(@rn := if(@t = type, @rn + 1,
if(@t := type, 1, 1)
)
) as rn
from data d cross join
(select @rn := 0, @t := -1) vars
order by type, rand()
) d cross join
(select count(*) as numtypes from types) as t
where rn <= 1000 / numtypes;
答案 1 :(得分:0)
这应该做你想要的(在包含TYPE=1
的100行,包含TYPE=2
的200行,包含TYPE=3
的300行,包含TYPE=4
的400行的表格上进行测试; 10
中的值_c / 10
,我得到40行,每行10个)。请检查性能,因为我显然使用的样本表比你真正的样本表少。
select * from
(select
@n := @n + 1 _n,
_c,
data.*
from
(select
type _t,
count(*) _c
from data
group by type) _1
inner join data on(_t = data.type)
inner join (select @n := 0) _2 order by data.type) _2
where mod(_n, floor(_c / 10)) = 0
order by type, id;
虽然每个组的编号相同,但不能保证从每个组中获取完全相同的编号,因为floor(_c / 10)
显然存在舍入不准确的情况。