【问题标题】:MYSQL: Group By regex patternMYSQL:按正则表达式模式分组
【发布时间】:2016-07-23 00:20:26
【问题描述】:

我正在尝试进行统计跟踪。在我的数据库中,我存储引用 url。我经常有类似以下的网址:

http://www2.trafficadbar.com/__a4w4
http://trafficadbar.com/__a4w4
http://www.trafficadbar.com/__a4w4
http://4acesmailer.com/credit_click.php?userid=2472&openkey=gbyp2vcm
http://4acesmailer.com/credit_click.php?userid=2714&openkey=gbyp2vcm
http://4acesmailer.com/credit_click.php?userid=2723&openkey=gbyp2vcm
http://4acesmailer.com/credit_click.php?userid=3245&openkey=gbyp2vcm
http://4acesmailer.com/credit_click.php?userid=3259&openkey=gbyp2vcm

我想知道如何对正则表达式模式执行 GROUP BY 和 COUNT。基本上我想要的返回如下:

trafficadbar 3
4acesmailer 5 

目前,当我尝试执行 GROUP BY 时,它仅适用于网址完全相同的情况。所以 www.blah.com 和 blah.com 是两个不同的结果,进一步每个 url 变量 ?blah=1&blahblah=2 充当另一个独特的组,

我已经搜索了无数的解决方案,但它们似乎大多针对所提出的问题非常具体,并且几乎所有似乎都显示了一些“非正则表达式”的解决方法——这很好......如果我能找到一种方法我可以申请。

【问题讨论】:

  • 我不常这么说,但你最好选择全部然后在 PHP 中解析它。
  • 我同意@AbraCadaver。我会在 PHP 中进行解析。

标签: php mysql regex pdo


【解决方案1】:

要从主机名中检索紧接在顶级域之前的部分,您可以这样做:

SELECT
  REVERSE(SUBSTRING(SUBSTRING_INDEX(rev_hostname, '.', 2),
          LOCATE('.', rev_hostname) + 1)
         ) domain
  , COUNT(id) hits
FROM (
  SELECT
    id
    , CONCAT(REVERSE(SUBSTRING_INDEX(SUBSTRING(referring_site, 8),
                                     '/', 1)), '.') rev_hostname
  FROM TestData
  ) T
GROUP BY domain
;

它:

  • 依靠所有 referring_sitehttp:// 开始,并且
  • 将失败 - 正如它所代表的那样 - 例如,4acesmailer.co.uk

如果需要,可以(在某种程度上)解决任何一个问题。

在行动中查看SQL Fiddle(您的数据经过一些调整/扩展以涵盖更多案例)。

如果需要调整/进一步详细信息,请发表评论。

【讨论】:

    【解决方案2】:

    如果您只关心这 2 个值,则可以使用以下方法:

    select case when yourcolumn like '%trafficadbar%' then 'trafficadbar' 
                when yourcolumn like '%4acesmailer%' then '4acesmailer' 
           end,
        count(*)
    from yourtable 
    group by 1
    

    编辑,考虑到您的 cmets,这可能会更加动态且相对容易扩展:

    select 
      replace(replace(replace(
         left(yourcolumn, locate('.com', yourcolumn) - 1), 
         'http://', ''), 
         'www.', ''), 
         'www2.', ''),
      count(*)
    from yourtable 
    group by 1
    

    【讨论】:

    • 我很确定这些只是示例,他们并不知道所有的域名。
    • @AbraCadaver -- 好点,如果 OP 正在搜索特定域或想要以某种方式聚合相似域,则可能并不完全清楚。不要认为仅靠 sql 就可以实现后者...
    • 我帖子中的“类似”这个词,绝对是说我不是专门寻找那些——这很容易:)
    • @Bruce -- 很公平,这对于单独的sql 来说并不是微不足道的。请参阅编辑以获取潜在的“工作”解决方案......
    • @sgeddes 这个answer 也许?身份证
    【解决方案3】:

    我不够熟练,无法在 SQL 中可靠地完成这一切;移动部分太多:可能有很多子域、可能有很多 TLD(不仅仅是.com)、可能有格式错误的域等等......

    我的方法: 选择所有内容并在 PHP 中解析。

    在下面的示例中,我假设 URL 位于 urls 列中,并且您有一个 date_added 列,其中包含每个 URL 添加到数据库时的日期时间。相应地调整您的查询。

    选择过去 30 天内添加的所有网址:

    SELECT `urls` FROM `myTable`
    WHERE `date_added` >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
    

    将所有结果放在$rows 数组中,然后对其进行处理以生成所需的报告

    $rows = [...];//Associative array of all rows returned by the query above
    $results = []; //will hold aggregate counts
    
    foreach($rows as $row){   
        $host = parse_url($row['urls'],PHP_URL_HOST); //eg: www2.trafficadbar.com
        $matches = [];
    
        //find top level domain or skip to next row
        if(!preg_match('/[^\.]*\.[^\.]+$/',$host,$matches)) continue;        
    
        $domain = $matches[0]; //eg: trafficadbar.com
    
        //increment the count for this domain in results
        if(!isset($results[$domain])) $results[$domain]=0;
        $results[$domain]++;
    }
    

    根据您在 OP 中提供的输入,$results 将是:

    [
        'trafficadbar.com' => 3,
        '4acesmailer.com' => 5,
    ]
    

    您会注意到,与您不同,我保留了 TLD(例如:.com.net...),因为 ebay.comebay.ph 是完全不同的域。我建议不要将它们混合成一个结果。

    Live demo

    【讨论】:

    • 好一个。但只需在循环中添加到数组,然后使用array_count values()
    • 我认为这不会更快,因为当我只执行一次时,您将遍历数组两次(首先是所有 url 以添加域,然后是所有结果以计算值)。
    【解决方案4】:

    虽然来自@BeetleJuice 的解决方案会奏效,而且可能比我选择的解决方案更可靠,但我还是选择了 SQL 解决方案

    SELECT 
       CASE WHEN SUBSTRING(referring_site, 1, 8) = 'http://w' 
          THEN SUBSTRING_INDEX((SUBSTRING_INDEX(referring_site, '.', 2)), '.', -1)
          ELSE SUBSTRING_INDEX((SUBSTRING_INDEX(referring_site, '.', 1)), '://', -1) 
       END 
    AS domain 
    FROM 
    ....
    

    缺点是它不带有http://w 而是一些http://random.sub.domain

    【讨论】:

    • 如果有人有更好的纯mysql解决方案,那就是我真正想要的。最终我想做的是计算“。”的数量。在 :// 和 / 之间,如果 2 执行第一个 substring_index ,则执行第二个。我只是不知道如何在“。”上获得和应用正确的计数。
    • (i) 重新表述是否正确:您希望顶级域(comorg 等)之前的部分? (从点的数量来看可能会产生误导:您想从 abc.def.ghi.com 获得什么?) (ii) 总是启动 http?跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多