2016-03-15 19 views
6

Mam dwie jednostki w mojej bazie danych, które są powiązane z wieloma związkami. Zastanawiałem się, jaki byłby najlepszy sposób wyliczenia, które jednostki mają najbardziej podobieństwa na tej podstawie?Dopasowywanie podobnych podmiotów na podstawie wielu do wielu relacji

Próbowałem robić count (*) z przecięciem, ale kwerenda trwa zbyt długo, aby uruchomić na każdym wpisie w mojej bazie danych (istnieje około 20k rekordów). Podczas wykonywania zapytania, które napisałem, użycie procesora skacze do 100%, a baza danych ma problemy z blokowaniem.

Oto kod pokazujący co próbowałem:

Moi stoły wyglądają coś wzdłuż tych linii:

/* 20k records */ 
create table Movie(
    Id INT PRIMARY KEY, 
    Title varchar(255) 
); 

/* 200-300 records */ 
create table Tags(
    Id INT PRIMARY KEY, 
    Desc varchar(255) 
); 

/* 200,000-300,000 records */ 
create table TagMovies(
    Movie_Id INT, 
    Tag_Id INT, 
    PRIMARY KEY (Movie_Id, Tag_Id), 
    FOREIGN KEY (Movie_Id) REFERENCES Movie(Id), 
    FOREIGN KEY (Tag_Id) REFERENCES Tags(Id), 
); 

(to działa, ale jest strasznie powolny) To jest zapytanie, które Napisałem, aby wypróbować i wymienić je: Zwykle filtruję również z top 1 & dodać klauzulę where, aby uzyskać określony zestaw powiązanych danych.

SELECT 
    bk.Id, 
    rh.Id 
FROM 
    Movies bk 
    CROSS APPLY (
     SELECT TOP 15 
      b.Id, 
      /* Tags Score */ 
      (
      SELECT COUNT(*) FROM (
       SELECT x.Tag_Id FROM TagMovies x WHERE x.Movie_Id = bk.Id 
       INTERSECT 
       SELECT x.Tag_Id FROM TagMovies x WHERE x.Movie_Id = b.Id 
       ) Q1 
      ) 
      as Amount 
     FROM 
      Movies b 
     WHERE 
      b.Id <> bk.Id 
     ORDER BY Amount DESC 
    ) rh 

Objaśnienie: Filmy mają znaczniki, a użytkownik może uzyskać spróbować znaleźć filmów podobnych do tego, który wybrali na podstawie innych filmów, które mają podobne tagi.

Odpowiedz

4

Hmm ... tylko pomysł, ale może nie zrobił zrozumieć ... Ta kwerenda powinien powrócić najlepiej dopasowane filmów według tagów dla danego filmu ID:

SELECT m.id, m.title, GROUP_CONCAT(DISTINCT t.Descr SEPARATOR ', ') as tags, count(*) as matches 
FROM stack.Movie m 
LEFT JOIN stack.TagMovies tm ON m.Id = tm.Movie_Id 
LEFT JOIN stack.Tags t ON tm.Tag_Id = t.Id 
WHERE m.id != 1 
AND tm.Tag_Id IN (SELECT Tag_Id FROM stack.TagMovies tm WHERE tm.Movie_Id = 1) 
GROUP BY m.id 
ORDER BY matches DESC 
LIMIT 15; 

EDIT: Właśnie uświadomiłem sobie, że to dla M $ SQL ... ale może coś podobnego można zrobić ...

+0

Dzięki. Skończyło się na tym, że napisałem podobne rozwiązanie z połączeniami. – newb

+0

Jaka jest wydajność? BTW. Myślę, że możesz zarobić pewną prędkość przez indeks dla tagu ID – barat

+0

1hr -> 40 sekund – newb

1

Powinieneś chyba zdecydować się na konwencję nazewnictwa i trzymać się jej. Czy tabele to rzeczowniki w liczbie pojedynczej czy w liczbie mnogiej? Nie chcę wchodzić w tę debatę, ale wybrać jedną lub drugą.

Bez dostępu do Twojej bazy danych nie wiem, jak to się stanie. To tylko z mojej głowy. Możesz także ograniczyć wartość o M.id, aby znaleźć najlepsze dopasowania dla pojedynczego filmu, co, jak sądzę, może poprawić nieco wydajność.

Ponadto, TOP x powinien pozwolić ci uzyskać najbardziej zbliżone x.

SELECT 
    M.id, 
    M.title, 
    SM.id AS similar_movie_id, 
    SM.title AS similar_movie_title, 
    COUNT(*) AS matched_tags 
FROM 
    Movie M 
INNER JOIN TagsMovie TM1 ON TM1.movie_id = M.movie_id 
INNER JOIN TagsMovie TM2 ON 
    TM2.tag_id = TM1.tag_id AND 
    TM2.movie_id <> TM1.movie_id 
INNER JOIN Movie SM ON SM.movie_id = TM2.movie_id 
GROUP BY 
    M.id, 
    M.title, 
    SM.id AS similar_movie_id, 
    SM.title AS similar_movie_title 
ORDER BY 
    COUNT(*) DESC 
+0

O ile mogę powiedzieć, powyższe rozwiązanie nie ma nawet zastosowania do T-SQL, a twoje jest jedynym prawidłowym rozwiązaniem. Inne rozwiązanie jest zawężone do jednego tytułu tylko wtedy, gdy dostajesz wszystkie naraz. Więc nie wiem, dlaczego inne rozwiązanie ma wszystkie punkty. – Ralph