0

在过去的两天里,我遇到了一个让我发疯的问题。我基本上有4个按以下顺序继承的表:

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

所以一个用户有很多博客,这些博客有很多页面和访问量。每个页面也属于一个类别。

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

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

我的查询如下:

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 数据库永远挂起,可能试图提取数百万行。我不是数据库专家,这很明显。有人能以某种方式指出我正确的方向,这样我就可以做出一个很好的查询,甚至可以在数百万条记录上表现良好(当然使用正确的硬件)?

4

2 回答 2

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,即使在加入 blogs 表上的子选择之后也是如此。

对于类别计数,您需要每个用户的不同类别计数,但挑战在于类别在关系层次结构(到 pages 表)中链接得很深,因此您必须创建一个单独的子选择来连接 user_id 而不是blog_id。

即使这个查询包含尽可能多的子选择,它仍然应该很快,因为没有两个子选择相互连接。只要在连接的任一侧都有一个索引表(子选择实际上是未索引的临时表),你应该没问题。

于 2012-07-31T16:27:58.547 回答
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、visitors_count、categories_count。

于 2012-07-31T16:05:19.620 回答