1

因此,我必须利用 SO 必须提供的最伟大的 mySQL 头脑的所有力量。我必须根据每条记录中的 IP 地址汇总详细记录。这是场景:

简而言之,我们有联盟想知道:“我联盟中的哪些学校观看了哪些视频多少次”?在 SQL 术语中,它相当于对详细记录进行计数,按它可能属于哪个 IP 范围进行分组。

  1. 我们有几个大学联盟——每个联盟都有几个不同的学校是成员。
  2. 联盟内的每所学校都使用不同的 IP 范围来访问我们为这些学校提供的视频。
  3. IP 范围使用通配符指定,因此每所学校都指定诸如“100.200.35.x、100.201.xx、100.202.39.50 等”之类的内容,每所学校的平均范围数为 10 或 15。
  4. 要汇总的原始文本日志文件已经在数据库中(每个日志条目一行),并且具有访问视频文件的实际 IP 地址。
  5. 有 100 条数以百万计的详细记录,所以我完全预计这是一个长期缓慢的过程,会运行相当长的时间。
  6. 存在可以将通配符“分解”到所代表的各个 IP 中的 PHP 脚本,但我担心这将是最终答案,可能需要数周时间才能运行。

为了简单起见,我只会参考被访问的视频文件名并计算它的日志条目,但实际上所有细节,如开始/停止/持续时间等都在那里,最终将成为一部分这个解决方案的。)

使用 Consortium 记录如下内容:(除日志详细信息外的所有表设计均可供建议):

| id|consortium   |
| 10|Ivy League   |
| 20|California   |

School/IP 记录如下内容:

|  id|school     |consortium_id|
| 101|Harvard    |10           |
| 102|Yale       |10           |
| 103|UCLA       |20           |
| 104|Berkeley   |20           |

| id|school_id|ip_range         |
|  1| 101     |100.200.x.x      |
|  2| 101     |100.201.65.x     |
|  3| 101     |100.202.39.50    |
|  4| 101     |100.202.39.51    |
|  5| 101     |100.200.x.x      |
|  6| 101     |100.201.65.x     |
|  7| 101     |100.202.39.50    |

详细记录如下:

|session     |ip_address     |filename          |
|560554790925|100.202.390.500|history101.mp4    |
|406417611526|43.22.90.5     |newsreel.mp4      |
|650423700223|100.202.39.50  |history101.mp4    |
|650423700223|100.202.50.12  |science101.mp4    |
|513057324209|100.202.39.56  |history101.mp4    |

我喜欢认为我对 mySQL 非常方便,但是这个正在扩展它,并且希望有人可以提供一个壮观的功能或一组步骤。

4

2 回答 2

2

使用您现有的数据结构,您可以按如下方式进行字符串匹配(但效率不高):

SELECT   schools.school, detail.filename, COUNT(*)
FROM     schools
    JOIN ipranges ON schools.id = ipranges.school_id
    JOIN detail   ON detail.ip_address LIKE REPLACE(ipranges.ip_range, 'x', '%')
WHERE    schools.consortium_id = ?
GROUP BY schools.school, detail.filename

更好的方法是将您的 IP 范围存储为网络地址和前缀长度:

ALTER TABLE ipranges
  ADD COLUMN network INT UNSIGNED,
  ADD COLUMN prefix  TINYINT;
UPDATE ipranges SET
  network = INET_ATON(REPLACE(ip_range, 'x', 0)),
  prefix  = 32 - 8*(CHAR_LENGTH(ip_range) - CHAR_LENGTH(REPLACE(ip_range,'x',''));
ALTER TABLE ipranges
  DROP COLUMN ip_range;

ALTER TABLE detail
  ADD COLUMN ip_address_new INT UNSIGNED;
UPDATE detail SET
  ip_address_new = INET_ATON(ip_address);
ALTER TABLE detail
  DROP COLUMN ip_address,
  CHANGE ip_address_new ip_address INT UNSIGNED;

那么它只是执行一些位比较的情况:

SELECT   schools.school, detail.filename, COUNT(*)
FROM     schools
    JOIN ipranges ON schools.id = ipranges.school_id
    JOIN detail   ON detail.ip_address & ~((1 << 32 - ipranges.prefix) - 1)
                   = ipranges.network
WHERE    schools.consortium_id = ?
GROUP BY schools.school, detail.filename
于 2012-05-31T16:34:18.510 回答
0
SELECT D.filename, S.school, COUNT(D.*)
FROM detail_records AS D
     INNER JOIN ip_map AS I ON D.ip_address LIKE CONCAT(SUBSTRING(I.ip_range, 1, LOCATE('x', I.ip_range)-1), '%')
     INNER JOIN school AS S ON S.id = I.school_id
     INNER JOIN consortium AS C ON C.id = S.consortium_id
WHERE S.consortium_id = <consortium identifier>
GROUP BY D.filename, S.school
于 2012-05-31T16:51:35.560 回答