针对大量数据的自定义排序算法

时间:2013-02-09 17:00:12

标签: php sql-server symfony solr doctrine-orm

我需要根据搜索查询以特定方式对大量数据进行排序,但我不确定采用的最佳方法。

我正在尝试排序的数据是按学校分组的课程列表。每门课程由一所学校授课。每所学校可能属于任何数量的“伙伴关系”,这代表了许多学校之间的关系。用户可以按课程名称搜索任意数量的课程。

我需要按如下方式对数据进行排序:

  • 课程按学校分组,每页出现10所学校。

  • 可以提供用户搜索过的每个课程的学校应首先出现在列表中。

  • 在这些结果之后,属于可以容纳用户搜索过的所有课程的合作关系的学校应该彼此相邻。

以下是一个例子:

  • A 教授历史,法语和英语课程。
  • B 教授法语和数学。
  • C 教授历史。
  • B C 是合伙人。
  • D 教授历史。

  • 用户搜索“历史记录”和“法语”。

  • 应该首先出现在结果中,包括历史和法语课程,因为它可以提供用户正在寻找的两个课程。

  • 接下来会出现
  • B ,然后是 C ,其后会列出相关课程,因为合作伙伴可以提供用户所需的两门课程。

  • D 接下来出现,因为它只提供1个相关课程。

数据存储在几个表的Microsoft SQL Server数据库中。这是一个简化的架构:

课程:

  • int id
  • varchar name
  • int schoolId

学校:

  • int id
  • varchar name

伙伴关系:

  • int id
  • varchar partnershipName

SchoolPartnership:

  • int id
  • int schoolId
  • int partnershipId

有超过100000门课程和约300所学校。我不知道如何对SQL中指定的课程进行排序,我认为这是我最大的问题。我只需要在每页显示10个结果,但由于我无法在SQL查询中进行排序,因此我必须提取整个结果集并在PHP中手动对其进行排序,然后才能将结果集减少到10个结果。

我目前正在使用Doctrine 2在多个连接的单个查询中提取我需要的数据,将结果保存为数组。然后计划是在PHP中操作这一大量记录,以使其按正确顺序排列。由于这个数组的大小,我担心这个排序过程会非常慢,所以我正在寻找关于如何更快地完成这个过程的建议:

  • 处理SQL查询中的排序。
  • 建议如何在诸如Solr之类的搜索引擎中实现所描述的算法(我对此基础知识有一点了解,但没有执行复杂的排序)。
  • 如果其他两个选项不可行,建议如何最好地在PHP中执行排序。

修改

我在这方面取得了一些进展,谢谢(尤其是@Neil)。我已经打开了一个单独的问题(Groupwise MAX() on a subquery),其中包含了我迄今为止的一些进展。

3 个答案:

答案 0 :(得分:0)

按匹配课程的数量查找学校很简单:

SELECT schoolId, COUNT(*) AS schoolCount
  FROM Courses
  WHERE name IN ('History', 'French')
  GROUP BY schoolId

如果这就是您所需的全部内容,您可以ORDER BY schoolCount DESC按照您想要的顺序获取它们。

要找到匹配课程的合作伙伴,首先需要找到至少在一所学校开设课程的合作伙伴关系:

SELECT partnershipId, COUNT(DISTINCT name) AS partnershipCount
  FROM SchoolPartnership
  INNER JOIN Courses ON Course.schoolId = SchoolPartnership.schoolId
  WHERE name IN ('History', 'French')
  GROUP BY partnershipId

请注意,DISTINCT是必需的,因为我们并不关心合作伙伴关系中有多少所学校。如果您没有DISTINCT,那么您可以使用子选择:

SELECT partnershipId, COUNT(*) AS partnershipCount
  FROM (
    SELECT DISTINCT partnershipId, name
      FROM SchoolPartnership
      INNER JOIN Courses ON Course.schoolId = SchoolPartnership.schoolId
      WHERE name IN ('History', 'French'))
  GROUP BY partnershipId

然后,您可以使用上面的第一个和最后一个查询作为与SchoolPartnership的联接中的子选择,以按partnerMatches和schoolMatches的降序排列学校。 (请注意,我假设所有学校都与至少一所学校合作。)我认为最终的查询将如下所示:

SELECT SchoolMatches.schoolID
  FROM (
    SELECT schoolId, COUNT(*) AS schoolCount
      FROM Courses
      WHERE name IN ('History', 'French')
      GROUP BY schoolId
  ) SchoolMatches
  JOIN SchoolPartnership ON SchoolMatches.schoolID = SchoolPartnership.schoolID
  JOIN (
    SELECT partnershipId, COUNT(DISTINCT name) AS partnershipCount
      FROM SchoolPartnership
      INNER JOIN Courses ON Course.schoolId = SchoolPartnership.schoolId
      WHERE name IN ('History', 'French')
      GROUP BY partnershipId
   ) PartnershipMatches ON SchoolPartnership.schoolId = PartnershipMatches.schoolId
   ORDER BY PartnershipMatches.partnershipCount DESC, SchoolMatches.SchoolCount DESC

答案 1 :(得分:0)

我们在网站的网页上遇到了类似的问题。我们使用所有参数创建了特殊的变性搜索表,以执行没有子查询或连接的搜索。所有数据都是重复的,因此当更改内容时,我们会更新所有denormalizar数据。我们使用后台任务来同步数据,因此搜索结果可能在一段时间内不实际。

可能看起来很复杂,但只有当你的数据和请求会成长时才会这样。

答案 2 :(得分:-2)

filter_var('sgamgee@example.com', FILTER_VALIDATE_EMAIL); // Returns "sgamgee@example.com"

这是一个有效的电子邮件地址。