我有一个包含IP范围的表ipaddresses
。列是:ipStart,ipEnd
的例子
ipStart: 3579374832
ipEnd: 3579374839
现在我想从另一个表中选择300个IP地址:visit_count
。基于这300个地址,我想检查IP地址是否在表ipaddresses
我目前的代码:
$results = $database->query("SELECT user_id,
DATE_FORMAT(last_visit, '%Y-%m-%d') as datumet,
cookieId, ipaddress
FROM `visit_count`
WHERE last_visit BETWEEN
'$firstDayOfMonth' AND '$lastDayOfMonth'
AND user_id = '$userId'
GROUP BY cookieId
ORDER BY last_visit ASC");
$rows = $database->loadObjectList($results);
foreach ( $rows as $row ) {
$ipaddressLong = ip2long($row->ipaddress);
// This is where its very very slow
$selO = $database->query("SELECT ipStart, ipEnd FROM `ipaddresses`
WHERE '$ipaddressLong' BETWEEN `ipStart` AND `ipEnd`");
$rowO = $database->getrow($selO);
}
结果是一个非常慢的查询,消耗了大量的cpu。
ipaddresses
包含ipStart
和ipEnd
的索引,包含大约5万行。
如何让它更快?
答案 0 :(得分:0)
使用JOIN对单个查询进行快速而脏的更改: -
SELECT a.ipStart, a.ipEnd
FROM `ipaddresses` a
INNER JOIN
(
SELECT user_id, DATE_FORMAT(last_visit, '%Y-%m-%d') as datumet, cookieId, inet_aton(ipaddress ) AS ipaddressLong
FROM `visit_count`
WHERE last_visit BETWEEN '$firstDayOfMonth' AND '$lastDayOfMonth'
AND user_id = '$userId'
GROUP BY cookieId
) b
ON b.ipaddressLong BETWEEN a.ipStart AND a.ipEnd
请注意,这可能会简化。但是,这取决于您使用 GROUP BY cookieId 。目前这将获得每个cookie id一行,但是哪一行是未定义的(即,可以是任何user_id,ip地址以及与该cookie id一起的最后访问日期)。
答案 1 :(得分:0)
您可以简化第一个选择。您不需要选择ipaddress
以外的任何字段。您也不需要订购或分组结果。如果您想要唯一的ipaddress
es,请使用distinct
select distinct ipaddress
from visit_count
where last_visit between '$firstDayOfMonth' and '$lastDayOfMonth'
and user_id = '$userId'
即使第二个表有50k行,300 p选择似乎也不多。在我的机器上,即使没有索引,也只需要几分之一秒。
如果您想要简化它,可以使用
join
两个表格
select ipstart, ipend
from ipaddresses i
join visit_count v on inet_aton(v.ipaddress) between i.ipstart and i.ipend
where v.last_visit between '$firstDayOfMonth' and '$lastDayOfMonth'
and v.userid = '$userId'
答案 2 :(得分:0)
1)使用比其他建议更好的查询
2)您是否为所需的字段定义了索引?
3)不知道你是否使用mysql或mysqli ...无论如何使用fetch_assoc比fetch_array更快
这3个技巧你应该没问题