[英]SQL: return user table with calculated column for match percentage?
我目前正在編寫一個基於回答問題匹配用戶的webapp。 我只在一個查詢中實現了我的匹配算法,並將其調整到目前為止需要8.2ms來計算2個用戶之間的匹配百分比。 但我的webapp必須獲取用戶列表並遍歷執行此查詢的列表。 對於5000個用戶,我的本地計算機需要50秒。 是否可以將所有內容放在一個查詢中,該查詢返回一個包含user_id的列和一個包含計算匹配的列? 或者存儲過程是一個選項?
我目前正在使用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
我很無聊,所以:這是你的查詢的重寫版本 - 基於模式的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引用。 你有一個可怕的嵌套查詢,但這只是一個格式挑戰。
變化:
我沒有優化查詢; 如我所寫,它在我的系統上以5ms運行。 在更大的數據上,您可能需要重構其中一些或使用一些技巧,例如將一些CTE子句轉換為SELECT ... INTO TEMPORARY TABLE
臨時表創建語句,然后在查詢之前進行索引。
您也可能希望將users
行集的生成移出CTE並轉換為scores
的FROM
子查詢子句。 這是因為WITH
需要表現為子句之間的優化范圍,因此數據庫必須實現行,並且不能使用向上或向下推送子句等技巧。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.