2012-01-10 167 views
2

我使用ASP.NET成员资格,最重要的是我添加了一些与用户有一对多关系的表。使用计数优化SQL查询

一个用户有很多笔,笔记本等

我编码一个自定义的成员提供和已经修改了选择查询检索所有关于用户以及用户具有笔的数量和信息“工具”的总数,但查询显然是灾难性的。 无论如何得到这些信息? 这是我曾尝试:

SET STATISTICS TIME ON 
SELECT m.Email, m.PasswordQuestion, m.IsApproved, m.CreateDate, m.LastLoginDate, 
     u.LastActivityDate, m.LastPasswordChangedDate, u.UserId, m.IsLockedOut, 
     m.LastLockoutDate, CAST(m.Comment as nvarchar(max)) as Comment, 
     COUNT(DISTINCT cl.Id) AS NumberOfPens, 
     COUNT(DISTINCT cf.Id)+ 
      COUNT(DISTINCT cm.Id) + 
      COUNT(DISTINCT ce.Id) + 
      COUNT(DISTINCT cl.Id) + 
      COUNT(DISTINCT cp.Id) + 
      COUNT(DISTINCT csl.Id)+ 
      COUNT(DISTINCT cs.Id) + 
      COUNT(DISTINCT cshl.Id) AS NumberOfTools 
FROM aspnet_Membership AS m 
     INNER JOIN aspnet_Users AS u ON m.UserId = u.UserId 
     INNER JOIN Pens AS cf ON u.UserId = cf.Owner 
     INNER JOIN Notebooks AS cm ON u.UserId = cm.Owner 
     INNER JOIN Rulers AS ce ON u.UserId = ce.Owner 
     INNER JOIN Calculators AS cl ON u.UserId = cl.Owner 
     CROSS JOIN aspnet_Applications AS a 
GROUP BY u.UserId, m.Email, m.PasswordQuestion, m.IsApproved, 
     m.CreateDate, m.LastLoginDate, u.LastActivityDate, 
     m.LastPasswordChangedDate, m.IsLockedOut, m.LastLockoutDate, 
     CAST(m.Comment as nvarchar(max)) 

SET STATISTICS TIME OFF 

9分钟11秒91级的用户!

+0

是的,我知道我吮吸DB。但我真的很想拥有这些数字,但我不知道如何获取这些数字 – BCartolo 2012-01-10 16:46:57

+0

您正在使用哪种SQL Server版本? – Quassnoi 2012-01-10 16:49:40

+0

Microsoft SQL Server 2008 R2标准 – BCartolo 2012-01-10 18:29:33

回答

2

作为一个快速打击,最终拿出cross join - 你没有使用它,它只是增加你的结果并放慢速度。

此外,你可以做接合前的计数,这将节省group by并允许RDBMS并行查询,像这样:

SELECT  
    m.Email, 
    m.PasswordQuestion, 
    m.IsApproved, 
    m.CreateDate, 
    m.LastLoginDate, 
    u.LastActivityDate, 
    m.LastPasswordChangedDate, 
    u.UserId, 
    m.IsLockedOut, 
    m.LastLockoutDate, 
    CAST(m.Comment as nvarchar(max)) as Comment, 
    PenCount AS NumberOfPens, 
    PenCount + NotebookCount + RulerCount + CalculatorCount AS NumberOfTools 
FROM 
    aspnet_Membership AS m 
    INNER JOIN aspnet_Users AS u ON 
     m.UserId = u.UserId 
    INNER JOIN (select owner, count(1) as PenCount from Pens group by owner) AS cf ON 
     u.UserId = cf.Owner 
    INNER JOIN (select owner, count(1) as NotebookCount from Notebooks group by owner) AS cm ON 
     u.UserId = cm.Owner 
    INNER JOIN (select owner, count(1) as RulerCount from Rulers group by owner) AS ce ON 
     u.UserId = ce.Owner 
    INNER JOIN (select owner, count(1) as CalculatorCount from Calculators group by owner) AS cl ON 
     u.UserId = cl.Owner 

需要注意的是,如果一个用户可能没有所有工具,你会想要做LEFT连接代替inner连接,然后将所有*Count列与coalesce(Count, 0)包装在一起。

+0

好的。我现在正在尝试。谢谢! – BCartolo 2012-01-10 16:42:50

+0

@BCartolo - 我刚刚编辑此查询的速度也更快一些。让我知道你在找什么:) – Eric 2012-01-10 16:46:50

+0

@BCartolo,你可能需要改变与count(1)相关联的表上的LEFT JOIN。如果您的笔拥有者不在笔内使用,但在笔记本,统治者等中使用,则会被忽略。通过对“count”别名执行LEFT JOIN,将确保所有成员都将包含在其各自的计数中,即使计数仅在一个此类子类别计数中。 – DRapp 2012-01-10 16:47:43

3
SELECT m.Email, m.PasswordQuestion, m.IsApproved, m.CreateDate, m.LastLoginDate, 
     u.LastActivityDate, m.LastPasswordChangedDate, u.UserId, m.IsLockedOut, 
     m.LastLockoutDate, CAST(m.Comment as nvarchar(max)) as Comment, 
     penCount, 
     notebookCount + rulerCount + calculatorCount AS toolCount 
FROM aspnet_Membership m 
JOIN aspnet_Users u 
ON  u.userId = m.userId 
CROSS APPLY 
     (
     SELECT COUNT(*) 
     FROM pens 
     WHERE owner = u.UserId 
     ) p (penCount) 
CROSS APPLY 
     (
     SELECT COUNT(*) 
     FROM notebooks 
     WHERE owner = u.UserId 
     ) n (notebookCount) 
CROSS APPLY 
     (
     SELECT COUNT(*) 
     FROM rulers 
     WHERE owner = u.UserId 
     ) r (rulerCount) 
CROSS APPLY 
     (
     SELECT COUNT(*) 
     FROM calculators 
     WHERE owner = u.UserId 
     ) c (calculatorCount) 
+0

谢谢我也会尝试这个选项 – BCartolo 2012-01-10 18:30:51

+0

这个选项的工作速度与其他选项一样快。但另一个被首先发布。不管怎么说,多谢拉! – BCartolo 2012-01-10 18:57:36