5

Ich schreibe gerade eine Webapp, die Benutzer basierend auf der beantworteten Frage entspricht. Ich habe meinen Matching-Algorithmus in nur einer Abfrage realisiert und so weit eingestellt, dass es 8,2 ms dauert, um den Übereinstimmungs-Prozentsatz zwischen 2 Benutzern zu berechnen. Aber meine Webanwendung muss eine Liste von Benutzern erstellen und die Liste durchlaufen, in der diese Abfrage ausgeführt wird. Für 5000 Benutzer dauerte es 50 Sekunden auf meinem lokalen Rechner. Ist es möglich, alles in eine Abfrage zu setzen, die eine Spalte mit der Benutzer-ID und eine Spalte mit der berechneten Übereinstimmung zurückgibt? Oder ist eine gespeicherte Prozedur eine Option?SQL: Benutzer-Tabelle mit berechneter Spalte für Übereinstimmungs-Prozentsatz zurückgeben?

Ich arbeite derzeit mit MySQL aber bereit Datenbanken zu wechseln, wenn nötig.

Für alle Interessierten in das Schema und die Daten, ich habe eine SQLFiddle erstellt: http://sqlfiddle.com/#!2/84233/1

und meine passende Abfrage:

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

Sie können die „häufig gestellte Fragen“ subexpression der beiden „Beine“ der Abfrage kombinieren. Sie könnten verallgemeinern auch die Unterabfragen für Benutzer = 1 und user = 101 in eine generali CTE-Abfrage (wenn Ihr DBMS suppoert sie aber zuerst:. Bitte zeigen Sie uns die Tabellendefinitionen und vielleicht einige Daten. – wildplasser

+0

Ja, Daten mit der jeweiligen gewünschten Ausgabe –

+1

Ich habe eine SQLFiddle zum Spielen erstellt :) Wenn ich Benutzer 1 und 5 übereinstimmte, sollte das Ergebnis '43 .678 'sein http://sqlfiddle.com/#!2/84233/1 – Mexxer

Antwort

1

mir langweilig wurde, so: Hier ist eine neu geschriebene Version der Abfrage - basierend auf einem PostgreSQL-Port Ihr Schema - die auf einmal die Spiele für alle Benutzer Paarungen berechnet:

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

Ich habe überprüft und es ergeben sich die gleichen Ergebnisse für das Benutzerpaar (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; 

Es gibt keinen Grund, warum Sie diese zurück zu MySQL nicht Port können, da der CTE zur besseren Lesbarkeit nur da ist und alles tut, nicht, dass Sie nicht mit FROM (SELECT ...) tun können. Es gibt keine WITH RECURSIVE Klausel und kein CTE wird von mehr als einem anderen CTE referenziert. Sie hätten eine etwas gruselig verschachtelte Abfrage, aber das ist nur eine Herausforderung bei der Formatierung.

Änderungen:

  • generieren eine Reihe von unterschiedlichen Benutzern
  • diesen Satz von verschiedenen Benutzern Selbst verbinden eine Reihe von Benutzer-Paarungen
  • zu erstellen und dann auf dieser Liste von Paarungen in der Partitur beitreten Abfrage eine Tabelle von Partituren
  • produzieren der Score-Tabelle zu erzeugen, indem die weitgehend doppelten Abfragen für possiblescore1 und possiblescore2, actualscore1 und actualscore2 kombiniert.
  • dann in der endgültigen äußeren Abfrage

I optimiert die Abfrage nicht zusammenfassen; Wie geschrieben läuft es in 5ms auf meinem System. Bei größeren Daten ist es möglich, einige CTE Klauseln in SELECT ... INTO TEMPORARY TABLE temporäre Tabellenerstellung Anweisungen, die Sie dann Index vor der Abfrage einen Teil davon oder Tricks restrukturieren müssen möglicherweise wie konvertieren.

Es ist auch möglich, dass Sie die Generierung des users Rowsets aus dem CTE und in eine FROM Unterabfrage von scores verschieben möchten. Das ist, weil WITH erforderlich ist als Optimierungs Zaun zwischen Klauseln verhalten, so muss die Datenbankzeilen materialisieren und nicht Tricks wie Klauseln nach oben oder nach unten drücken.