5

मैं वर्तमान में एक वेबपैप लिख रहा हूं जो उत्तर प्रश्न के आधार पर उपयोगकर्ताओं से मेल खाता है। मैंने अपने मिलान करने वाले एल्गोरिदम को केवल एक प्रश्न में महसूस किया है और इसे अब तक ट्यून किया है कि 2 उपयोगकर्ताओं के बीच मिलान प्रतिशत की गणना करने में 8.2ms लगते हैं। लेकिन मेरे वेबपैप को इस क्वेरी को करने वाली सूची के माध्यम से उपयोगकर्ताओं की एक सूची लेनी है और फिर से शुरू करना है। 5000 उपयोगकर्ताओं के लिए यह मेरी स्थानीय मशीन पर 50sec लिया। क्या सब कुछ एक प्रश्न में रखना संभव है जो उपयोगकर्ता_आईडी के साथ एक कॉलम और गणना के साथ एक कॉलम देता है? या एक संग्रहीत प्रक्रिया एक विकल्प है?एसक्यूएल: मिलान प्रतिशत के लिए गणना कॉलम के साथ उपयोगकर्ता तालिका लौटाएं?

मैं वर्तमान में MySQL के साथ काम कर रहा हूं लेकिन यदि आवश्यक हो तो डेटाबेस स्विच करने के इच्छुक हूं। 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 के लिए एक सामान्यीकृत सीटीई क्वेरी में उपकुंजी को सामान्यीकृत भी कर सकते हैं (यदि आपका डीबीएमएस उन्हें suppoert। लेकिन पहले: कृपया हमें टेबल परिभाषाएं और शायद कुछ डेटा दिखाएं। – wildplasser

+0

हां, संबंधित वांछित आउटपुट के साथ डेटा –

+1

मैंने साथ खेलने के लिए एक SQLFiddle बनाया है :) जब मैं उपयोगकर्ता 1 और 5 से मेल खाता हूं तो परिणाम '43.678 'होना चाहिए http://sqlfiddle.com/#!2/84233/1 – Mexxer

उत्तर

1

मैं ऊब गया था, इसलिए: यहाँ आपकी क्वेरी की एक फिर से लिखा संस्करण है

स्कीमा और डेटा में रुचि किसी के लिए, मैं एक SQLFiddle बना लिया है - कि एक ही बार में सभी उपयोगकर्ता-सी जोड़ी के लिए मैच की गणना करता है - अपने स्कीमा के एक 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 खंड नहीं है और एक से अधिक सीटीई से सीटीई का संदर्भ नहीं दिया गया है। आपके पास एक डरावनी घोंसला वाली क्वेरी होगी, लेकिन यह सिर्फ एक स्वरूपण चुनौती है।

परिवर्तन:

  • अलग उपयोगकर्ताओं के उस समूह में शामिल होने के स्व
  • उपयोगकर्ता जोड़ी का एक सेट बनाने के लिए अलग-अलग उपयोगकर्ताओं का एक सेट उत्पन्न और उसके बाद स्कोर में जोड़ी की उस सूची में शामिल होने पर स्कोर
  • की तालिका बनाने के लिए क्वेरी possiblescore1 और possiblescore2, actualscore1 और actualscore2 के लिए बड़े पैमाने पर डुप्लिकेट क्वेरीज़ को जोड़कर स्कोर तालिका का उत्पादन करें।
  • तब अंतिम बाहरी क्वेरी

मैं क्वेरी अनुकूल नहीं है में यह संक्षेप में प्रस्तुत; जैसा कि लिखा है यह मेरे सिस्टम पर 5ms में चलता है। बड़े डेटा पर यह संभव है कि आपको इनमें से कुछ को पुन: स्थापित करने की आवश्यकता हो या कुछ सीटीई खंडों को SELECT ... INTO TEMPORARY TABLE अस्थायी तालिका निर्माण कथन में परिवर्तित करने जैसे चालों का उपयोग करने की आवश्यकता हो, जिसे आप क्वेरी करने से पहले अनुक्रमित करते हैं।

यह भी है कि आप CTE के बाहर और scores के FROM सबक्वेरी खंड में users rowset की पीढ़ी ले जाना चाहते हैं जाएगा संभव है। ऐसा इसलिए है क्योंकि WITH को क्लॉज के बीच ऑप्टिमाइज़ेशन बाड़ के रूप में व्यवहार करने की आवश्यकता है, इसलिए डेटाबेस को पंक्तियों को मूर्त रूप देना चाहिए और क्लॉज को ऊपर या नीचे धक्का देने जैसी चाल का उपयोग नहीं कर सकते हैं।

संबंधित मुद्दे