5

我目前正在编写一个基于回答问题匹配用户的web应用程序。我已经在一个查询中实现了我的匹配算法,并将其调整到目前为止,计算两个用户之间的匹配百分比需要8.2ms。但是,我的web应用程序必须获取用户列表并遍历执行此查询的列表。对于5000个用户,我的本地计算机花费了50秒。是否有可能将所有内容都放入一个查询中,该查询返回一个包含user_id和一列的计算匹配的列?或者是一个存储过程的一个选项?SQL:返回用户表与计算列的匹配百分比?

我目前正在使用MySQL,但愿意切换数据库,如果需要的话。

任何有兴趣的架构和数据,我创建了一个SQLFiddle:http://sqlfiddle.com/#!2/84233/1

和我匹配的查询:

SELECT COALESCE(SQRT((100.0*as1.actual_score/ps1.possible_score) * (100.0*as2.actual_score/ps2.possible_score)) - (100/ps1.commonquestions), 0) AS perc 
    FROM (SELECT SUM(imp.value) AS actual_score 
     FROM user_questions AS uq1 
     INNER JOIN importances imp ON imp.id = uq1.importance 
     INNER JOIN user_questions uq2 ON uq2.question_id = uq1.question_id AND uq2.user_id = 101 
     AND (uq1.accans1 = uq2.answer_id 
      OR uq1.accans2 = uq2.answer_id 
      OR uq1.accans3 = uq2.answer_id 
      OR uq1.accans4 = uq2.answer_id) 
     WHERE uq1.user_id = 1) AS as1, 
    (SELECT SUM(value) AS possible_score, COUNT(*) AS commonquestions 
     FROM user_questions AS uq1 
     INNER JOIN importances ON importances.id = uq1.importance 
     INNER JOIN user_questions uq2 ON uq1.question_id = uq2.question_id AND uq2.user_id = 101 
     WHERE uq1.user_id = 1) AS ps1, 
    (SELECT SUM(imp.value) AS actual_score 
     FROM user_questions AS uq1 
     INNER JOIN importances imp ON imp.id = uq1.importance 
     INNER JOIN user_questions uq2 ON uq2.question_id = uq1.question_id AND uq2.user_id = 1 
     AND (uq1.accans1 = uq2.answer_id 
      OR uq1.accans2 = uq2.answer_id 
      OR uq1.accans3 = uq2.answer_id 
      OR uq1.accans4 = uq2.answer_id) 
     WHERE uq1.user_id = 101) AS as2, 
    (SELECT SUM(value) AS possible_score 
     FROM user_questions AS uq1 
     INNER JOIN importances ON importances.id = uq1.importance 
     INNER JOIN user_questions uq2 ON uq1.question_id = uq2.question_id AND uq2.user_id = 1 
     WHERE uq1.user_id = 101) AS ps2 
+1

您可以结合查询的两个“腿”的“常见问题”子表达式。你也可以将用户= 1和用户= 101的子查询概括为一个广义的CTE查询(如果你的数据库管理系统支持它们),但首先:请向我们展示表格定义和可能的一些数据。 – wildplasser

+0

是的,数据与各自所需的输出 –

+1

我创建了一个SQLFiddle来玩:)当我匹配用户1和5时,结果应该是'43.678'http://sqlfiddle.com/#!2/84233/1 – Mexxer

回答

1

我很无聊,所以:这是你查询的重写版本 - 根据您的架构的一个PostgreSQL端口 - 计算比赛为所有用户配对一次:

http://sqlfiddle.com/#!12/30524/6

我检查了它,并为用户对(1,5)产生了相同的结果。

WITH 
userids(uid) AS (
    select distinct user_id from user_questions 
), 
users(u1,u2) AS (
    SELECT u1.uid, u2.uid FROM userids u1 CROSS JOIN userids u2 WHERE u1 <> u2 
), 
scores AS (
     SELECT 
      sum(CASE WHEN uq2.answer_id IN (uq1.accans1, uq1.accans2, uq1.accans3, uq1.accans4) THEN imp.value ELSE 0 END) AS actual_score, 
      sum(imp.value) AS potential_score, 
      count(1) AS common_questions, 
      users.u1, 
      users.u2 
     FROM user_questions AS uq1 
     INNER JOIN importances imp ON imp.id = uq1.importance 
     INNER JOIN user_questions uq2 ON uq2.question_id = uq1.question_id 
     INNER JOIN users ON (uq1.user_id=users.u1 AND uq2.user_id=users.u2) 
     GROUP BY u1, u2 
), 
score_pairs(u1,u2,u1_actual,u2_actual,u1_potential,u2_potential,common_questions) AS (
    SELECT s1.u1, s1.u2, s1.actual_score, s2.actual_score, s1.potential_score, s2.potential_score, s1.common_questions 
    FROM scores s1 INNER JOIN scores s2 ON (s1.u1 = s2.u2 AND s1.u2 = s2.u1) 
    WHERE s1.u1 < s1.u2 
) 
SELECT 
    u1, u2, 
    COALESCE(SQRT((100.0*u1_actual/u1_potential) * (100.0*u2_actual/u2_potential)) - (100/common_questions), 0) AS "match" 
FROM score_pairs; 

没有理由你不能端口这回的MySQL,因为CTE只有那里可读性,没有做任何事情你不能做FROM (SELECT ...)。没有WITH RECURSIVE子句,并且多个其他CTE都没有引用CTE。你会有一个可怕的嵌套查询,但这只是一个格式化的挑战。

变化:

  • 生成一组不同用户的
  • 自加入该组不同的用户创建一组用户配对
  • ,然后配对在比分名单上加入查询以生成分数表
  • 通过结合possiblescore1和possiblescore2,actualscore1和actualscore2的大量重复查询来生成分数表。
  • 然后总结它在最终的外部查询

我还没有优化的查询;就像写入它在我的系统上运行5ms。在更大的数据上,您可能需要重构其中的一些或使用诸如将某些CTE子句转换为SELECT ... INTO TEMPORARY TABLE临时表创建语句的技巧,然后在查询之前将其创建索引。

您可能还想将生成的users行集移出CTE,并将其转换成FROM子查询子句scores。这是因为WITH需要作为子句之间的优化栅栏,所以数据库必须实现行并且不能使用诸如向上或向下推动子句的技巧。