0

我有一个我记录了其 IP 的访问者列表。现在,对于特定报告,我需要查看每个访问者来自哪个国家/地区。

我将此任务分为两部分,首先获取唯一登录的用户的所有 IP,第二部分是使用国家/IP 信息(来自 ip2nation)搜索两个表,并使用提供的 IP 获取国家/地区。

第一部分 - 获取唯一登录的用户的所有 IP

SELECT
     a.uid, a.hostname, a.timestamp, 
 COUNT(*) AS times
FROM
  login_activity a
GROUP BY
  a.hostname
ORDER BY
  times desc

这给了我所有过去登录用户的 IP(主机名)。工作正常。

第二部分 - 通过输入 IP 从两个表(都有数千条记录)中获取国家/地区

SELECT 
    c.country
FROM 
    ip2nationCountries c, ip2nation i
WHERE 
    i.ip < INET_ATON(  "157.191.122.36" ) 
AND 
    c.code = i.country

ORDER BY i.ip DESC 
LIMIT 0 , 1

这也很好用。

现在,对于真正的问题。加入这两个查询,从所有登录用户那里获取国家(而不是 IP)。这是我写的:-

        SELECT
         a.uid, a.hostname, a.timestamp, c.country, 
         COUNT(*) AS times
        FROM
          login_activity a, ip2nationCountries c, ip2nation i
        WHERE
           i.ip < INET_ATON(a.hostname)     
           AND c.code = i.country

        GROUP BY
          a.hostname
        ORDER BY
          times desc;

这有两个问题:-

  • 加载需要很长时间。
  • 它给出了错误的数据(每行显示数千次访问)。
  • 基本上,它显示所有数据都是错误的。

你能帮我做这个 SQL 吗?

以防万一,表格的结构/数据如下:-

表格的结构/数据是:-

ip2nation(有很多数据)

(结构)

CREATE TABLE ip2nation (
  ip int(11) unsigned NOT NULL default '0',
  country char(2) NOT NULL default '',
  KEY ip (ip)
);

(数据)

INSERT INTO ip2nation (ip, country) VALUES(0, 'us');
INSERT INTO ip2nation (ip, country) VALUES(687865856, 'za');
INSERT INTO ip2nation (ip, country) VALUES(689963008, 'eg');
INSERT INTO ip2nation (ip, country) VALUES(691011584, 'za');
INSERT INTO ip2nation (ip, country) VALUES(691617792, 'zw');
INSERT INTO ip2nation (ip, country) VALUES(691621888, 'lr');
INSERT INTO ip2nation (ip, country) VALUES(691625984, 'ke');
INSERT INTO ip2nation (ip, country) VALUES(691630080, 'za');
INSERT INTO ip2nation (ip, country) VALUES(691631104, 'gh');
INSERT INTO ip2nation (ip, country) VALUES(691632128, 'ng');
INSERT INTO ip2nation (ip, country) VALUES(691633152, 'zw');
INSERT INTO ip2nation (ip, country) VALUES(691634176, 'za');
INSERT INTO ip2nation (ip, country) VALUES(691650560, 'gh');
INSERT INTO ip2nation (ip, country) VALUES(691666944, 'ng');
INSERT INTO ip2nation (ip, country) VALUES(691732480, 'tz');
INSERT INTO ip2nation (ip, country) VALUES(691798016, 'zm');
INSERT INTO ip2nation (ip, country) VALUES(691863552, 'za');
INSERT INTO ip2nation (ip, country) VALUES(691994624, 'zm');
INSERT INTO ip2nation (ip, country) VALUES(692011008, 'za');
INSERT INTO ip2nation (ip, country) VALUES(692027392, 'mg');
INSERT INTO ip2nation (ip, country) VALUES(692035584, 'ao');
INSERT INTO ip2nation (ip, country) VALUES(692043776, 'na');
INSERT INTO ip2nation (ip, country) VALUES(692060160, 'eg');
INSERT INTO ip2nation (ip, country) VALUES(692191232, 'ci');
INSERT INTO ip2nation (ip, country) VALUES(692207616, 'za');
INSERT INTO ip2nation (ip, country) VALUES(692240384, 'gh');
INSERT INTO ip2nation (ip, country) VALUES(692256768, 'sd');

ip2nationCountries(有很多数据)

(结构)

CREATE TABLE ip2nationCountries (
  code varchar(4) NOT NULL default '',
  iso_code_2 varchar(2) NOT NULL default '',
  iso_code_3 varchar(3) default '',
  iso_country varchar(255) NOT NULL default '',
  country varchar(255) NOT NULL default '',
  lat float NOT NULL default '0',
  lon float NOT NULL default '0',  
  PRIMARY KEY  (code),
  KEY code (code)
);

(数据)

INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('ad', 'AN', 'AND', 'Andorra', 'Andorra', 42.3, 1.3);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('ae', 'AR', 'ARE', 'United Arab Emirates', 'United Arab Emirates', 24, 54);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('af', 'AF', 'AFG', 'Afghanistan', 'Afghanistan', 33, 65);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('ag', 'AT', 'ATG', 'Antigua and Barbuda', 'Antigua and Barbuda', 17.03, -61.48);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('ai', 'AI', 'AIA', 'Anguilla', 'Anguilla', 18.15, -63.1);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('al', 'AL', 'ALB', 'Albania', 'Albania', 41, 20);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('am', 'AR', 'ARM', 'Armenia', 'Armenia', 40, 45);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('an', 'AN', 'ANT', 'Netherlands Antilles', 'Netherlands Antilles', 12.15, -68.45);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('ao', 'AG', 'AGO', 'Angola', 'Angola', -12.3, 18.3);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('aq', 'AT', 'ATA', 'Antarctica', 'Antarctica', -90, 0);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('ar', 'AR', 'ARG', 'Argentina', 'Argentina', -34, -64);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('as', 'AS', 'ASM', 'American Samoa', 'American Samoa', -14.2, -170);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('at', 'AU', 'AUT', 'Austria', 'Austria', 47.2, 13.2);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('au', 'AU', 'AUS', 'Australia', 'Australia', -27, 133);
INSERT INTO ip2nationCountries (code, iso_code_2, iso_code_3, iso_country, country, lat, lon) VALUES('aw', 'AB', 'ABW', 'Aruba', 'Aruba', 12.3, -69.58);

登录活动

(结构)

CREATE TABLE IF NOT EXISTS `mslop_login_activity` (
  `aid` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for an activity (session).',
  `uid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'The mslop_users.uid corresponding to a session, or 0 for anonymous user.',
  `host_user_agent` varchar(256) NOT NULL DEFAULT '' COMMENT '$_SERVER["HOST_USER_AGENT"] string. This can be used with get_browser() in PHP.',
  `hostname` varchar(128) NOT NULL DEFAULT '' COMMENT 'The IP address that was used for this session.',
  `timestamp` int(11) NOT NULL DEFAULT '0' COMMENT 'The UNIX timestamp when the session was started.',
  PRIMARY KEY (`aid`),
  KEY `aid` (`aid`),
  KEY `uid` (`uid`),
  KEY `timestamp` (`timestamp`)
);

(数据)

INSERT INTO `mslop_login_activity` (`aid`, `uid`, `host_user_agent`, `hostname`, `timestamp`) VALUES
(1, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0', '172.24.1.143', 1363038356),
(2, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0', '172.24.1.143', 1363038374),
(3, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17', '172.24.1.143', 1363193841),
(4, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17', '172.24.1.143', 1363194789),
(5, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17', '172.24.1.143', 1363197889),
(6, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/536.26.17 (KHTML, like Gecko) Version/6.0.2 Safari/536.26.17', '172.24.1.143', 1363207361),
(7, 35, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0', '172.24.1.143', 1363301612),
(8, 35, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0', '172.24.1.143', 1363301751),
(9, 1, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0', '172.24.1.143', 1363364574),
(10, 1, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17', '172.24.1.143', 1363374517),
(11, 1, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17', '172.24.1.143', 1363377701),
(12, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17', '172.24.1.143', 1363714792),
(13, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17', '172.24.1.143', 1363714911),
(14, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17', '172.24.1.143', 1363714929),
(15, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0', '172.24.1.143', 1363715946),
(16, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.28.10 (KHTML, like Gecko) Version/6.0.3 Safari/536.28.10', '172.24.1.161', 1363791080),
(17, 4, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.28.10 (KHTML, like Gecko) Version/6.0.3 Safari/536.28.10', '172.24.1.161', 1363791124),
(18, 1, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.28.10 (KHTML, like Gecko) Version/6.0.3 Safari/536.28.10', '172.24.1.161', 1363791144),
(19, 3, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22', '172.24.1.143', 1363791365),
(20, 64, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.152 Safari/537.22', '172.24.1.143', 1363791650);

我将查询更改如下,但它仍然显示错误的结果...

你们可以看看:-

SELECT l.uid,
    l.hostname,
    l.timestamp,
    c.country,
    l.times
FROM ip2nationCountries c
JOIN ip2nation i ON c.code = i.country
JOIN ( SELECT
        a.uid,
        a.hostname,
        MAX(a.timestamp) AS timestamp,
        COUNT(*) AS times
    FROM mslop_login_activity a
    WHERE a.uid = 3
    AND a.hostname = "157.191.122.36"
    GROUP BY a.hostname) AS l ON i.ip < INET_ATON( l.hostname )
4

2 回答 2

0

这将为您提供用户 ID、主机名以及他们登录该主机的次数。

SELECT a.uid, a.hostname, COUNT(*) AS times
FROM mslop_login_activity a
GROUP BY  a.uid, a.hostname
ORDER BY  times desc

为了简化获取国家/地区的过程,请使用视图来计算范围。(此观点高度依赖于您是否拥有有关 IP 地址如何映射到国家/地区的准确和完整的数据。我没有尝试验证您的数据。)

create view ip_range as 
select t1.ip as ip_start
        , (select min(ip) - 1 from ip2nation where ip > t1.ip) ip_end
        , t1.country
from ip2nation t1

现在您应该能够使用简单、明显的连接来获取国家/地区。

SELECT a.uid
          , a.hostname
          , inet_aton(a.hostname) as ip_int
          , ip_range.country
FROM mslop_login_activity a
inner join ip_range
on inet_aton(a.hostname) between ip_range.ip_start and ip_range.ip_end

使用您的示例数据,这将不返回任何行。一方面,您用于登录活动的所有 IP 地址都在保留范围内。另外,“ip2nation”中最长的IP地址(整数)为9位;那是不够的数字。我自己的 IP 地址转换为 10 位整数。

如果我在登录活动中将您的一个 IP 地址更新为转换为 9 位整数的美国 IP 地址,则上述查询正确地将国家识别为“美国”。


这是您的原始查询之一。它不像你认为的那样做。

SELECT
     a.uid, a.hostname, a.timestamp, 
 COUNT(*) AS times
FROM
  login_activity a
GROUP BY
  a.hostname
ORDER BY
  times desc

它返回这一行。

uid  hostname      timestamp  times
--
3    172.24.1.143  1363038356 20

但是这个查询返回 12 行。请注意,12 行不等于 20 次。

SELECT uid
FROM mslop_login_activity 
where uid = 3

在 MySQL 中使用 GROUP BY 是危险的,除非您知道自己在做什么并且您真的非常非常小心。(我认为任何其他 dbms 都不会运行您原来的第一个查询,因为它不符合 SQL。)

于 2013-06-21T22:55:24.737 回答
0

您缺少一个连接。如果您使用正确的连接语法,这将更加明显,因此请将此作为一个教训,并在将来始终使用joinandon子句。

您想要的查询:

    SELECT a.uid, a.hostname, a.timestamp, c.country, COUNT(*) AS times
    FROM login_activity a left outer join
         ip2nationCountries c
         on a.hostname = c.ip left outer join
         ip2nation i
         on i.ip < INET_ATON(a.hostname) AND c.code = i.country
    GROUP BY a.hostname
    ORDER BY times desc;

两条评论。我做了这些left outer join。如果有不匹配的主机名,那么它们仍然会出现在输出中(inner join如果您想过滤掉它们,请更改为)。login_activity其次,如果表真的很大,您可能希望在加入其他表之前对其进行预聚合。

于 2013-06-21T20:39:59.637 回答