sql选择了很多计数

时间:2012-07-31 15:59:20

标签: mysql

过去两天我有一个问题让我疯了。我基本上有4个表继承按以下顺序:

             users
               |
categories   blogs
     |      |     |
     ---- pages visits

因此,用户拥有许多博客,其中包含许多页面和访问次数。每个页面也属于一个类别。

我想要的是提取具有以下关联计数的所有用户:

  1. 每个用户拥有的博客总数
  2. 每个用户拥有的总页数
  3. 每个用户拥有博客的类别总数
  4. 每个用户的总访问次数
  5. 每个用户拥有的访问者总数(访问次数,但我们按不同的ip_address计算)
  6. 我的查询如下:

    SELECT
        u.id
        u.username,
        COUNT(b.id) as blogs_count,
        COUNT(p.id) as pages_count,
        COUNT(v.id) as visits_count,
        COUNT(distinct ip_address) as visitors_count
        COUNT(c.id) as categories_count
    FROM
        users u
    LEFT JOIN
        blogs b ON(b.user_id=u.id)
    LEFT JOIN
        pages p ON(p.blog_id=b.id)
    LEFT JOIN
        visits v ON(v.blog_id=b.id)
    LEFT JOIN
        categories c ON(v.category_id=c.id)
    GROUP BY u.id, blogs_count, pages_count, visits_count, 
             visitors_count, categories_count
    

    我应该让24个用户获得他们的计数但是,鉴于我有近300,000次访问,我得到了我的SQL数据库永远可能会挂起数百万行。 我不是数据库大师,这很明显。有人能指出我正确的方向,所以我可以做出一个好的查询,甚至可以在数百万条记录上表现良好(当然还有合适的硬件)?

2 个答案:

答案 0 :(得分:0)

SELECT
    u.id
    u.username,
    COUNT(b.id) as blogs_count,
    COUNT(p.id) as pages_count,
    COUNT(v.id) as visits_count,
    COUNT(distinct ip_address) as visitors_count
    COUNT(c.id) as categories_count
FROM
    users u
LEFT JOIN
    blogs b ON(b.user_id=u.id)
LEFT JOIN
    pages p ON(p.blog_id=b.id)
LEFT JOIN
    visits v ON(v.blog_id=b.id)
LEFT JOIN
    categories c ON(v.category_id=c.id)
GROUP BY u.id

尝试按照规定从您的论坛中删除blogs_count,pages_count,visits_count,visitor_count,categories_count。

答案 1 :(得分:0)

试试这个:

SELECT    u.id,
          u.username,
          COUNT(b.id)                     AS blogs_count,
          COALESCE(MAX(p.pagecnt), 0)     AS pages_count,
          COALESCE(MAX(v.visitscnt), 0)   AS visits_count,
          COALESCE(MAX(v.visitorscnt), 0) AS visitors_count,
          COALESCE(MAX(c.catcnt), 0)      AS categories_count
FROM      users u
LEFT JOIN blogs b ON u.id = b.user_id
LEFT JOIN (
          SELECT   blog_id, 
                   COUNT(*) AS pagecnt
          FROM     pages
          GROUP BY blog_id
          ) p ON b.id = p.blog_id
LEFT JOIN (
          SELECT   blog_id, 
                   COUNT(*) AS visitscnt, 
                   COUNT(DISTINCT ip_address) AS visitorscnt
          FROM     visits
          GROUP BY blog_id
          ) v ON b.id = v.blog_id
LEFT JOIN (
          SELECT   aa.id,
                   COUNT(DISTINCT dd.id) AS catcnt
          FROM     users aa
          JOIN     blogs bb ON aa.id = bb.user_id
          JOIN     pages cc ON bb.id = cc.blog_id
          JOIN     categories dd ON cc.category_id = dd.id
          GROUP BY aa.id
          ) c ON u.id = c.id
GROUP BY  u.id, 
          u.username

击穿

这也适用于不同的DBMS,如PGSQL,SQL-Server等。

挑战在于你有这种1:M关系的层次结构,将它们连接在一起可以很容易地抛弃不同类型的计数(因为你想要在某些地方使用不同的计数,但在其他地方需要总计数)。

我决定做的是首先选择每个页面的计数并访问/区分访问者,按blog_id进行分组。这确保了每个blog_id只能获得一行,即使在加入博客表上的子选择后也是如此。

对于类别计数,您需要为每个 用户 计算不同的类别,但挑战是类别在关系层次结构中深入链接(到页面表) ),所以你必须创建一个单独的subselect加入user_id而不是blog_id。

即使该查询包含的子选择数量很多,它仍然应该非常快,因为没有两个子选择相互连接。只要在连接的任一侧有一个索引表(子选择实际上是未编入索引的临时表),您应该没问题。