SQL排序,分页,过滤ASP.NET中的最佳实践

时间:2010-09-13 14:00:53

标签: asp.net mysql sorting filter paging

我想知道Google是如何做到的。在页数和结果总数方面,我有很多慢查询。 Google会在几分之一秒内返回250,000,00的计数值。

我正在处理网格视图。我为gridview构建了一个自定义分页器,它需要SQL查询根据用户设置的过滤器返回页数。过滤器至少为5,其中包括关键字,类别和子类别,日期范围过滤器以及用于排序的排序表达式过滤器。该查询包含大约10个大型表左连接。

每次执行搜索时执行此查询,并且查询执行平均持续30秒 - 无论是计数还是选择。我相信让它变慢的是我的查询字符串包含和独占日期范围过滤器。我已将(< =,> =)替换为BETWEEN和AND,但我仍遇到同样的问题。

在此处查看查询: http://friendpaste.com/4G2uZexRfhd3sSVROqjZEc

我遇到长日期范围参数问题。

检查包含日期的表格: http://friendpaste.com/1HrC0L62hFR4DghE6ypIRp

更新 [9/17/2010]我最小化了日期查询并删除了时间。  我尝试减少我的计数查询的连接(我实际上遇到了我的过滤器计数问题,这需要很长时间才能返回60k行的结果)。

      SELECT COUNT(DISTINCT esched.course_id)
        FROM courses c
           LEFT JOIN events_schedule esched
              ON c.course_id = esched.course_id
           LEFT JOIN course_categories cc
              ON cc.course_id = c.course_id
           LEFT JOIN categories cat
              ON cat.category_id = cc.category_id
     WHERE     1 = 1
           AND c.course_type = 1
           AND active = 1
           AND c.country_id = 52
           AND c.course_title LIKE '%cook%'
           AND cat.main_category_id = 40
           AND cat.category_id = 360
 AND (

    (2010-09-01' <= esched.date_start OR 2010-09-01' <= esched.date_end) 
    AND

    ('2010-09-25' >= esched.date_start OR '2010-09-25' >= esched.date_end)     
   )

我刚注意到,当我在主要或子类别字段上有过滤器时,我的查询速度非常快。但是,当我只有一个日期过滤器并且范围是一个月或一周时,它需要计算很多行并且平均在30秒内完成。

这些是静态字段:

AND c.course_type = 1
AND active = 1
AND c.country_id = 52

更新 [9/17/2010]如果为这三个字段创建一个哈希并将其存储在一个字段上,它会改变速度吗?

这些是我的动态字段:

AND c.course_title LIKE '%cook%'
AND cat.main_category_id = 40
AND cat.category_id = 360
// ?DateStart and ?DateEnd

更新 [9/17/2010]。现在我的问题是LIKE查询中的前导%

将发布更新的解释

2 个答案:

答案 0 :(得分:3)

像谷歌这样的搜索引擎使用非常复杂的幕后算法来搜索索引。从本质上讲,他们已经确定了每个页面上出现的单词以及这些单词的相对重要性以及页面的相对重要性(相对于其他页面)。这些索引非常快,因为它们基于按位索引

考虑以下谷歌搜索:

custom : 542 million google hits
pager : 10.8 m
custom pager 1.26 m

基本上他们所做的是创建了一个单词custom的记录,在该记录中,他们为包含它的每个页面放置了1,为每个不包含它的页面放置了0。然后他们拉上它,因为有比0更多的0。他们对寻呼机做同样的事情。

当搜索custom pager进入时,它们解压缩两个记录,对它们执行按位AND,这会产生一个位数组,其中length是它们已编入索引的页面总数和1的数量表示搜索的命中数。每个位的位置对应于预先知道的特定结果,并且它们只需要查找第一页的全部细节以显示在第一页上。

这是过于简单的,但这是一般原则。

哦,是的,他们还有大量服务器执行索引,大量服务器响应搜索请求。巨大的服务器库!

这使得它们比在关系数据库中完成的任何事情都快得多。

现在,针对您的问题:您可以粘贴一些示例SQL供我们查看吗?

您可以尝试的一件事是更改表和联接在SQl语句中出现的顺序。我知道它似乎不应该有所作为,但它当然可以。如果在语句的前面放置了最严格的连接,那么最终可能会在数据库中执行更少的总连接。

一个真实世界的例子。假设您想要在名称为“Johnson”的电话簿中找到所有条目,其数字以“7”开头。一种方法是查找以7开头的所有数字,然后将其与属于名为'Johnson'的人的数字联系起来。实际上,即使您对名称和数字都进行了索引,以相反的方式执行过滤也会快得多。这是因为'Johnson'这个名字比7号更具限制性。

因此,订单确实有效,并且数据库软件并不总是善于提前确定哪些连接首先执行。我不确定MySQL,因为我的经验主要是SQL Server,它使用索引统计来计算执行连接的顺序。经过多次插入,更新和删除后,这些统计信息已过期,因此必须定期重新计算这些统计信息。如果MySQL有类似的东西,你可以试试这个。

<强>更新 我查看了您发布的查询。十个左连接并不罕见,只要你有正确的索引就可以正常运行。你的不是一个复杂的查询。

您需要做的是将此查询分解为其基础。注释掉查找联接,例如货币,course_stats,国家,州和城市以及select语句中的相应字段。它仍然运行缓慢吗?可能不是。但它可能仍然不理想。

所以请注释掉所有其他内容,直到您按照课程ID和课程顺序获得课程和小组。然后,尝试添加左连接以查看哪个具有最大影响。然后,关注对性能影响最大的那些,更改查询的顺序。这是试错法。您可以更好地查看要加入的列上的索引。

例如,行cm.method_id = c.method_id将需要course_methodologies.method_id上的主键和courses.method_id上的外键索引,依此类推。此外,where,group by和order by子句中的所有字段都需要索引。

祝你好运

更新2 您真的需要查看此查询的日期过滤。你想做什么?

   AND ((('2010-09-01 00:00:00' <= esched.date_start
          AND esched.date_start <= '2010-09-25 00:00:00')
         OR ('2010-09-01 00:00:00' <= esched.date_end
             AND esched.date_end <= '2010-09-25 00:00:00'))
        OR ((esched.date_start <= '2010-09-01 00:00:00'
             AND '2010-09-01 00:00:00' <= esched.date_end)
            OR (esched.date_start <= '2010-09-25 00:00:00'
                AND '2010-09-25 00:00:00' <= esched.date_end)))

可以重写为:

AND (

    //date_start is between range - fine
    (esched.date_start BETWEEN '2010-09-01 00:00:00' AND '2010-09-25 00:00:00') 

    //date_end is between range - fine
    OR (esched.date_end BETWEEN '2010-09-01 00:00:00' AND '2010-09-25 00:00:00')       

    OR (esched.date_start <= '2010-09-01 00:00:00' AND esched.date_end >= '2010-09-01 00:00:00' ) 

    OR (esched.date_start <= '2010-09-25 00:00:00' AND esched.date_end > = '2010-09-25 00:00:00')
  )

答案 1 :(得分:2)

在您的更新中,您提到您怀疑问题出在日期过滤器中。

所有这些日期检查都可以在一张支票中总结出来:

esched.date_ends >= '2010-09-01 00:00:00' and esched.date_start <= '2010-09-25 00:00:00'

如果使用上述内容表现相同,请检查以下内容是否快速返回/正在选择索引:

SELECT COUNT(DISTINCT esched.course_id) FROM events_schedule esched WHERE esched.date_ends&gt; ='2010-09-01 00:00:00'和esched.date_start&lt; ='2010-09-25 00:00:00'

ps我认为在使用联接时,您可以执行SELECT COUNT( c.course_id )直接计算查询中课程的主要记录,即可能不需要那种不同的方式。


现在更新后大部分时间都会在更改后进行外卡搜索:

使用mysql full text search。确保检查fulltext-restrictions,一个重要的是它仅在MyISAM表中受支持。我必须说我还没有真正使用过mysql全文搜索,而且我不确定这会如何影响查询中其他索引的使用。

如果你不能使用全文搜索,imho你很幸运使用你当前的方法,即因为它不能使用常规索引来检查它包含在文本的任何部分中的单词。

如果是这种情况,您可能希望切换方法的特定部分并引入基于标记/关键字的方法。与类别不同,您可以为每个项目分配多个,因此其灵活但没有自由文本问题。