这个复杂的查询在MySQL中是否可行,还是需要PHP?

时间:2014-06-25 01:57:46

标签: php mysql

我正在计划一个数据库驱动的网站,根据用户如何回答问题来匹配用户。我认为最好的方法是在SELECT查询中运行匹配计算,但我不知道如何编写查询。

假设我有一个名为 user_answer 的表格,它看起来像这样:

+--------+-------------+--------+------------------+--------+
| userid | question_id | answer | preferred_answer | weight |
+--------+-------------+--------+------------------+--------+
| 1      | 20          | 3      |                  | 0      |
| 1      | 24          | 3      | 2, 3             | 1      |
| 1      | 36          | 2      | 2                | 10     |
| 1      | 37          | 3      | 1, 2, 3          | 50     |
| 1      | 40          | 3      | 3                | 250    |
| 2      | 20          | 3      | 3                | 10     |
| 2      | 24          | 3      | 2                | 1      |
| 2      | 25          | 2      |                  | 0      |
| 2      | 26          | 2      |                  | 0      |
| 2      | 40          | 3      | 2                | 250    |
+--------+-------------+--------+------------------+--------+

我想通过 match_percentage 进行选择和排序 - match_percentage应以这种方式计算:

  1. 鉴于userid = 1( current_user
  2. 选择匹配question_id的用户( match_user userid = 2)
  3. total_weight1 = 匹配的question_id 权重的总和 的 CURRENT_USER
  4. 如果 match_user 回答位于 current_user preferred_answer match1_weight = < strong> match1_weight + current_user的权重
  5. total_weight2 =匹配 question_id 重量的总和 的 match_user
  6. 如果 current_user 回答位于 match_user preferred_answer 中, match2_weight = match2_weight + 权重 match_user
  7. match_percentage = sqrt(( match1_weight / total_weight1 )* (的 match2_weight / total_weight2 ))
  8. 我不知道这是否可行。我期待DB变得非常大,所以加载它们并在PHP中进行计算可能不是最佳选择 - 但如果我错了,请纠正我。

    是否可以在查询中进行所有这些计算?

1 个答案:

答案 0 :(得分:1)

是的,我相信所有指定的计算都可以在查询中执行。

假设(userid,questionid)是UNIQUE,我们首先找到带有“匹配”问题的userid。我们可以通过这样的查询来做到这一点:

SELECT u.answer
     , u.preferred_answer
     , u.weight
     , m.userid           AS m_userid
     , m.question_id      AS m_question_id
     , m.answer           AS m_answer
     , m.preferred_answer AS m_preferred_answer
     , m.weight           AS m_weight
  FROM user_answer u
  JOIN user_answer m
    ON m.question_id = u.question_id
   AND m.userid <> u.userid
   AND u.userid = 1 
 ORDER
    BY m.userid
     , m.question_id

一旦我们完成了这项工作,我们就可以从这些工作中获得总重量和计算结果。

假设preferred_answer列是VARCHAR类型,并且包含逗号分隔的元素列表,没有空格,例如'2''2,3,5',您可以使用MySQL FIND_IN_SET函数返回列表中特定元素的索引位置。如果找不到“匹配”,那将返回0。

我相信这个查询符合规范。

SELECT m.userid           AS m_userid
     , SUM(u.weight)      AS total_weight1
     , SUM(IF(FIND_IN_SET(m.answer,u.preferred_answer),u.weight,0)) AS match1_weight
     , SUM(m.weight)      AS total_weight2
     , SUM(IF(FIND_IN_SET(u.answer,m.preferred_answer),m.weight,0)) AS match2_weight
     , SQRT(
         ( SUM(IF(FIND_IN_SET(m.answer,u.preferred_answer),u.weight,0)) / SUM(u.weight) )
       * ( SUM(IF(FIND_IN_SET(u.answer,m.preferred_answer),m.weight,0)) / SUM(m.weight) )
       ) AS match_percentage
  FROM user_answer u
  JOIN user_answer m
    ON m.question_id = u.question_id
   AND m.userid <> u.userid
   AND u.userid = 1 
 GROUP
    BY m.userid
 ORDER
    BY match_percentage DESC

注:

这些查询仅限桌面检查。我没有设置SQL小提琴进行测试。

第4项似乎是 current_user权重的总和,但仅包括匹配的答案。如果没有匹配的答案,我们将返回0.对于第6项也是如此,但只是反过来。)

如果userid 1和其他一些userid之间没有匹配的问题,则不会为其他用户ID返回任何行。

对于大型套装,这可能会持续一段时间。合适的覆盖指数应该可以提高性能。

为了提高查询性能,您可能需要考虑将此查询的结果“缓存”到单独的表中。只有在插入,更新,删除原始表中的行时,才需要刷新“缓存”表的内容。以前计算的结果对于正常访问可能仍然“足够好”。

如果您存储了结果,那么您还希望将u.userid作为SELECT列表和GROUP BY中的列返回。