用户表:user
+
--
---------+------------------+------+-----+---------------------+----------------+
¦ Field ¦ Type ¦
Null
¦
Key
¦
Default
¦ Extra ¦
+
--
---------+------------------+------+-----+---------------------+----------------+
¦ ID ¦
int
(
10
) unsigned ¦ NO ¦ PRI ¦
NULL
¦ auto_increment ¦
¦ userid ¦
varchar
(
30
) ¦ NO ¦ ¦ ¦ ¦
+
--
---------+------------------+------+-----+---------------------+----------------+
ID是用户id,userid是用户名
文章表:archives
+
--
-----------+------------------+------+-----+---------+----------------+
¦ Field ¦ Type ¦
Null
¦
Key
¦
Default
¦ Extra ¦
+
--
-----------+------------------+------+-----+---------+----------------+
¦ ID ¦
int
(
11
) unsigned ¦ NO ¦ PRI ¦
NULL
¦ auto_increment ¦
¦ click ¦
int
(
11
) unsigned ¦ NO ¦ ¦
0
¦ ¦
¦ title ¦
varchar
(
80
) ¦ NO ¦ MUL ¦ ¦ ¦
¦ pubdate ¦
int
(
11
) ¦ NO ¦ ¦
0
¦ ¦
¦ adminID ¦
int
(
11
) ¦ NO ¦ ¦
0
¦ ¦
¦ maxpage ¦
int
(
4
) ¦ YES ¦ ¦
1
¦ ¦
+
--
-----------+------------------+------+-----+---------+----------------+
ID是文章的id,自增,click是总点击量,title是文章标题,pubdate是发表日期,adminID是发表此文章的用户id。maxpage是文章的分页数量,对应user表的ID字段
访问记录表:access
+
--
---------+--------------------+------+-----+---------+----------------+
¦ Field ¦ Type ¦
Null
¦
Key
¦
Default
¦ Extra ¦
+
--
---------+--------------------+------+-----+---------+----------------+
¦ id ¦
int
(
4
) unsigned ¦ NO ¦ PRI ¦
NULL
¦ auto_increment ¦
¦ aid ¦
int
(
4
) unsigned ¦ NO ¦ ¦ ¦ ¦
¦ ipaddress ¦
varchar
(
15
) ¦ YES ¦ MUL ¦
NULL
¦ ¦
¦ time ¦
bigint
(
4
) unsigned ¦ NO ¦ ¦ ¦ ¦
+
--
---------+--------------------+------+-----+---------+----------------+
id是此表的自动编号,自增,aid是文章id,对应文章表的ID,ipaddress是此次访问的ip地址,time为访问时间,类型是unix时间戳。
这三张表的关系为:user表的ID对应archives表的adminId,archives的ID对应access表的aid。
我现在写了个存储过程,用来计算从:
2007年9月24日到2007年11月15日(共52天)
所有用户(大概20个)
所发的所有点击量> =100的文章(共3831篇)
每篇文章每一天所带来的独立ip数量
把上述结果集插入到一张新表里面。
新表结构如下:
editor_record:
+
--
--------+--------------------+------+-----+---------+-------+
¦ Field ¦ Type ¦
Null
¦
Key
¦
Default
¦ Extra ¦
+
--
--------+--------------------+------+-----+---------+-------+
¦ uid ¦
int
(
10
) ¦ NO ¦ ¦ ¦ ¦
¦ username ¦
varchar
(
30
) ¦ NO ¦ ¦ ¦ ¦
¦ aid ¦
int
(
10
) unsigned ¦ NO ¦ ¦ ¦ ¦
¦ title ¦
varchar
(
80
) ¦ NO ¦ ¦ ¦ ¦
¦ pubdate ¦
int
(
11
) ¦ NO ¦ ¦
0
¦ ¦
¦ maxpage ¦
int
(
4
) ¦ YES ¦ ¦
NULL
¦ ¦
¦ click ¦
int
(
11
) unsigned ¦ NO ¦ ¦
0
¦ ¦
¦ union_ip ¦
int
(
11
) unsigned ¦ NO ¦ ¦
0
¦ ¦
¦ time ¦
bigint
(
4
) unsigned ¦ NO ¦ ¦ ¦ ¦
+
--
--------+--------------------+------+-----+---------+-------+
uid为发文章的用户id,username为发文章的用户名称,aid是文章id,title是文章标题,pubdate是文章发表时间,maxpage是文章的分页数量,click为文章的总点击量,union_ip是独立ip数量,time是日期。
这么做的目的是为了我前台一个php页面能够单表很迅速的查出我想要的数据,这个表有几个字段是冗余的,但我为了php可以快速进行单表查询,所以冗余就冗余吧。
计算一篇文章独立ip的方法为:
select
access.aid,access.time,
count
(
distinct
(FROM_UNIXTIME(time,
'
%Y-%m-%d
'
)))
as
cnt
from
`access`
where
aid
=
文章id
group
by
c.aid
-
独立ip的算法是:如果一个ip地址在同一天内出现两次,只记一次,否则记它出现的次数。这个sql执行后,再sum一下cnt就是独立ip的数量。这张access的表有大概一百万条记录。
整个程序执行过程我们写了个存储过程,代码如下:
DELIMITER $$

DROP
PROCEDURE
IF
EXISTS
`mztest`.`proc_test1`$$
CREATE
PROCEDURE
`mztest`.`proc_test1` ()

BEGIN
DECLARE
a
int
(
11
);
DECLARE
b
INT
;
DECLARE
days
int
(
11
);
DECLARE
date
int
(
11
);
DECLARE
cur_1
CURSOR
FOR
Select
aid
from
tmp_table;
DECLARE
CONTINUE
HANDLER
FOR
NOT
FOUND
SET
b
=
1
;
CREATE
TEMPORARY
TABLE
tmp_table (
uid
int
(
10
)
NOT
NULL
,
username
varchar
(
30
)
NOT
NULL
,
aid
int
(
10
) unsigned
NOT
NULL
,
title
varchar
(
80
)
NOT
NULL
,
pubdate
int
(
11
)
NOT
NULL
default
0
,
maxpage
int
(
4
),
click
int
(
11
) unsigned
NOT
NULL
default
0
)TYPE
=
HEAP
DEFAULT
CHARSET
=
gbk;

SET
days
=
ABS
(
DATEDIFF
(FROM_UNIXTIME("
1190563200
","
%
Y
-%
m
-%
d
%
H:
%
i:
%
s"),FROM_UNIXTIME("
1195142399
","
%
Y
-%
m
-%
d
%
H:
%
i:
%
s")));
SET
date
=
1190563200
;
insert
into
tmp_table
Select
b1.ID
as
uid,b1.userid,a1.ID
as
aid,a1.title,a1.pubdate,a1.maxpage,a1.click
From
archives a1,
user
b1
Where
a1.adminid
=
b1.ID
And
a1.click
>=
100
;
WHILE
days
>
0
DO
SET
b
=
2
;
OPEN
cur_1;
REPEAT
FETCH
cur_1
INTO
a;
IF
b
=
2
THEN
insert
into
editor_record(uid,username,aid,title,pubdate,maxpage,click,union_ip,time)
select
t.uid,t.username,c.aid,t.title,t.pubdate,t.maxpage,t.click,
sum
(cnt)
as
union_ip,date
from
(
select
access.aid,access.time,
count
(
distinct
(FROM_UNIXTIME(time,
'
%Y-%m-%d
'
)))
as
cnt
from
`access`
where
aid
=
a
And
time
>=
date
And
time
<=
(date
+
86399
)
group
by
ipaddress) c,tmp_table t
where
c.aid
=
t.aid
and
t.uid
=
1
group
by
c.aid;
END
IF
;
UNTIL b
=
1
END
REPEAT;
CLOSE
cur_1;
SET
date
=
date
+
86400
;
SET
days
=
days
-
1
;
END
WHILE
;
END
;$$

DELIMITER ;
之前,我是用PHP直接计算这些结果的,选出两个日期,和一个编辑的名称,计算这段时间段内的薪水,速度及其的慢,
而且这个表在我们网站的服务器上,查的次数多了,对网站的速度影响都是很大,领导斥令我把这个统计程序暂时不对兼职编辑开放.让我尽快采取优化方案.
于是,我想到了使用linux下的计划任务,定时在每天12点自动执行一段脚本,计算出当天的独立ip数量和编辑的薪水.第一次执行的时候我想先计算出9月24日那天到今天这段时间的数据,以后每一天计算当天的数据就可以了.把最终的结果插入到一个新表里面,我的php在前台单表查询就可以了,这样肯定会很快.其实流程就是:
把复杂的数据计算过程完全交给mysql来做,php用来把结果显示出来.
可是...虽然运行结果是正确的。但是执行速度非常之慢,预计要执行几天吧.也太长久了...
算了一下,大概要执行1000000*3831*52次!!!
一开始打算用perl写的,最近在学这个语言,和php比较像,用起来应该会比较顺手,想正好可以拿来练练.但是我在linux配了一天的DBI(DBD是perl连接mysql接口,DBI是mysql的驱动),始终就是配置不成功.身边也没有有相关经验的人.后来想到用linux的shell来写,但shell语言对复杂数据的处理能力有限,而且在我感觉语法有些怪异.于是就用mysql的存储过程来写,
目前一直在琢磨优化代码的方案,我的压力很大啊,一堆兼职编辑在等着我这个程序给人家结算薪水呢,数据有错或者不能尽快给人家结果的话,我良心过不去啊,唉