Классификация SQL - PullRequest
       32

Классификация SQL

6 голосов
/ 16 февраля 2012

У меня есть система, которая отслеживает, какие документы просматривают пользователи.Каждый документ имеет свой идентификатор и кластер, к которому он принадлежит.Моя система отслеживает идентификатор сеанса и количество просмотров.Теперь я хотел бы создать запрос SQL, который бы дал мне два столбца - идентификатор сеанса и классифицированный кластер.Алгоритм классификации прост:

1. select all sessions
2. for each session S
   I. prepare an accumulator ACC for clusters
   II. select the clusters of viewed documents for this session
   III. for each cluster C accumulate the cluster count ( ACC[C]++ )
   IV. find the maximum in the ACC. That is the cluster that the session was classified to

Структуры таблиц следующие, я использую MySQL 5.5.16:

Session

+-------+-----------+--------------------+
| ID    | sessionID | classified_cluster |
+-------+-----------+--------------------+

SessionDocument

+-------+-----------+------------+
| ID    | sessionID | documentID |
+-------+-----------+------------+

Кластер

+-------+-------+
| ID    | label |
+-------+-------+

ClusterDocument

+-------+-----------+------------+
| ID    | clusterID | documentID |
+-------+-----------+------------+

Таким образом, в основном я хочу выбрать кластеры для каждого сеанса, подсчитать вхождение каждого кластера для просмотренных документови найти максимальное вхождение.Тогда идентификатор кластера, который произошел больше всего, является результатом сеанса, поэтому окончательный набор результатов содержит идентификатор сеанса и наиболее произошедший кластер:

Результат

+-----------+-----------------------+
| sessionID | classifiedIntoCluster |
+-----------+-----------------------+

Мне удалосьчтобы получить кластеры просмотренных документов для каждого сеанса (шаг 2 / II.) с помощью этого запроса:

SELECT SD.session_id, CD.cluster_id 
FROM cluster_document AS CD 
INNER JOIN session_document AS SD 
ON CD.document_id = SD.document_id
WHERE session_id IN (SELECT session_id FROM session) 

У меня проблемы с выяснением остальных.Возможно ли это даже с вложенными запросами SELECT?Должен ли я использовать курсор, и если да, может ли кто-нибудь показать пример с курсором?Любая помощь будет высоко ценится.

EDIT # 1: добавлена ​​реализация C #, дамп MySQL и ожидаемый результат

реализация C #

    private void ClassifyUsers() {
        int nClusters = Database.SelectClusterCount(); //get number of clusters
        DataSet sessions = Database.SelectSessions(); //get all sessions
        foreach (DataRow session in sessions.Tables[0].Rows) { //foreach session
           int[] acc = new int[nClusters]; //prepare an accumulator for each known cluster
           string s_id = session["session_id"].ToString();
           DataSet sessionClusters = Database.SelectSessionClusters(s_id); //get clusters for this session

           foreach (DataRow cluster in sessionClusters.Tables[0].Rows) { //for each cluster
               int c = Convert.ToInt32(cluster["cluster_id"].ToString()) - 1;
               acc[c]++; //accumulate the cluster count
           }

           //find the maximum in the accumulator -> that is the most relevant cluster
           int max = 0;
           for (int j = 0; j < acc.Length; j++) {
               if (acc[j] >= acc[max]) max = j;
           }
           max++;
           Database.UpdateSessionCluster(s_id, max); //update the session with its new assigned cluster
       }
    }

Структура таблицы, данные испытаний и ожидаемый результат

Структура таблицы и данные испытаний

Ожидаемый результат

EDIT # 2: добавлен меньший набор данных и дальнейшее прохождение алгоритма

Вот меньший набор данных:

SESSION

session id    |  cluster
abc                 0
def                 0
ghi                 0
jkl                 0       
mno                 0

CLUSTER

cluster_id  | label
1               A
2               B
3               C
4               D
5               E

SESSION_DOCUMENT

id      | session_id    |   document_id
1           abc             1
2           def             5
3           jkl             3
4           ghi             4
5           mno             2
6           def             2
7           abc             5
8           ghi             3

CLUSTER_DOCUMENT

id      | cluster_id    |   document_id
1           1                  2
2           1                  3
3           2                  5
4           3                  5
5           3                  1
6           4                  3
7           5                  2
8           5                  4

Подробный алгоритм

Шаг 1: получение кластеров для документов, просматриваемых сеансом

session_id  |  cluster_id   | label     | document_id   
abc             3               C           1
abc             2               B           5
abc             3               C           5
-----
def             2               B           5
def             3               C           5   
def             1               A           2
def             5               E           2   
----
ghi             5               E           4   
ghi             1               A           3   
ghi             4               D           3   
----
jkl             1               A           3   
jkl             4               D           3   
----
mno             1               A           2
mno             5               E           2

Шаг 2: количество вхождений кластеров

session_id |    cluster_id  | label |   occurrence
abc             3               C           2   <--- MAX
abc             2               B           1
----
def             2               B           1
def             3               C           1   
def             1               A           1
def             5               E           1   <--- MAX
----
ghi             5               E           1   
ghi             1               A           1   
ghi             4               D           1   <--- MAX
----
jkl             1               A           1   
jkl             4               D           1   <--- MAX
----
mno             1               A           1   
mno             5               E           1   <--- MAX

Шаг 3 (конечный результат): найти максимальное количество возникших кластеров для каждого сеанса (см. Выше) и построить окончательный набор результатов (session_id, cluster_id):

session_id |    cluster_id  
abc                 3           
def                 5
ghi                 4
jkl                 4
mno                 5

РЕДАКТИРОВАТЬ # 3: Принято разъяснение ответа

Оба приведенных ответа являются правильными.Они оба обеспечивают решение проблемы.Я дал Мости Мостачо принятый ответ, потому что он первым поставил решение и предоставил другую версию решения с VIEW.Решение от mankuTimma того же качества, что и решение Мости Мостачо.Поэтому у нас есть два одинаково хороших решения, я выбрал Моста Мостачо, потому что он был первым.

Спасибо им обоим за вклад.,

Ответы [ 2 ]

2 голосов
/ 16 февраля 2012

Что ж, у меня есть некоторые сомнения относительно того, как выбрать случай, когда их много, но, глядя на код C #, кажется, что этот выбор недетерминирован.

Теперь, учитывая данные выборки Шаг 2фактически приводит к:

+------------+------------+-------+------------+
| SESSION_ID | CLUSTER_ID | LABEL | OCCURRENCE |
+------------+------------+-------+------------+
| abc        |          3 | C     |          2 |
| def        |          1 | A     |          1 |
| def        |          2 | B     |          1 |
| def        |          3 | C     |          1 |
| def        |          5 | E     |          1 |
| ghi        |          1 | A     |          1 |
| ghi        |          4 | D     |          1 |
| ghi        |          5 | E     |          1 |
| jkl        |          1 | A     |          1 |
| jkl        |          4 | D     |          1 |
| mno        |          1 | A     |          1 |
| mno        |          5 | E     |          1 |
+------------+------------+-------+------------+

Итак, продолжая с этими данными, я получаю идентификатор сеанса и максимум (cluster_id) для этого идентификатора сеанса, в результате чего:

+------------+------------+
| SESSION_ID | CLUSTER_ID |
+------------+------------+
| abc        |          3 |
| def        |          5 |
| ghi        |          5 |
| jkl        |          4 |
| mno        |          5 |
+------------+------------+

Макс (cluster_id) как раз для выполнения этого недетерминированного выбора.Это запрос:

select s1.session_id, max(s1.cluster_id) as cluster_id from (
  select sd.session_id, cd.cluster_id, count(*) as Occurrence
  from session_document sd
  join cluster_document cd
  on sd.document_id = cd.document_id
  join cluster c
  on c.cluster_id = cd.cluster_id
  group by sd.session_id, cd.cluster_id, c.label
) as s1
left join (
  select sd.session_id, count(*) as Occurrence
  from session_document sd
  join cluster_document cd
  on sd.document_id = cd.document_id
  join cluster c
  on c.cluster_id = cd.cluster_id
  group by sd.session_id, cd.cluster_id, c.label
) as s2
on s1.session_id = s2.session_id and s1.occurrence < s2.occurrence
where s2.occurrence is null
group by s1.session_id

Возможно, добавление представления повысит производительность (замена вышеуказанного запроса):

create view MaxOccurrences as (
  select sd.session_id, cd.cluster_id, count(*) as Occurrence
  from session_document sd
  join cluster_document cd
  on sd.document_id = cd.document_id
  join cluster c
  on c.cluster_id = cd.cluster_id
  group by sd.session_id, cd.cluster_id, c.label
);

select s1.session_id, max(s1.cluster_id) as cluster_id
from MaxOccurrences as s1
left join MaxOccurrences as s2
on s1.session_id = s2.session_id and s1.occurrence < s2.occurrence
where s2.occurrence is null
group by s1.session_id

Дайте мне знать, если это работает.

2 голосов
/ 16 февраля 2012

Если я правильно понимаю вашу проблему, для каждого сеанса вы хотите кластер с наибольшим количеством просмотров документов.Итак, начнем. Запрос ниже возвращает максимальное количество или количество вхождений определенного идентификатора кластера для каждого идентификатора сеанса.

SELECT SESSION_ID,MAX(CNT) MAX_CNT
FROM (SELECT SD.SESSION_ID, CD.CLUSTER_ID,COUNT(*) AS CNT
FROM CLUSTER_DOCUMENT AS CD 
INNER JOIN SESSION_DOCUMENT AS SD 
ON CD.DOCUMENT_ID = SD.DOCUMENT_ID
GROUP BY SD.SESSION_ID,CD.CLUSTER_ID) CNT1
GROUP BY SESSION_ID

Затем объедините вышеуказанный результат с подзапросомвычисление количества) еще раз, чтобы получить идентификатор кластера максимального вхождения.Если есть два идентификатора кластера с одинаковым количеством вхождений, я использую идентификатор кластера с максимальным значением.Я проверил на ваших данных, и это работает.Кроме того, теперь этот запрос должен работать для всех баз данных.

SELECT B.SESSION_ID, MAX(CNT2.CLUSTER_ID) FROM 
(SELECT SESSION_ID,MAX(CNT) MAX_CNT
FROM (SELECT SD.SESSION_ID, CD.CLUSTER_ID,COUNT(*) AS CNT
FROM CLUSTER_DOCUMENT AS CD 
INNER JOIN SESSION_DOCUMENT AS SD 
ON CD.DOCUMENT_ID = SD.DOCUMENT_ID
GROUP BY SD.SESSION_ID,CD.CLUSTER_ID) CNT1
GROUP BY SESSION_ID) B
JOIN (SELECT SD.SESSION_ID, CD.CLUSTER_ID,COUNT(*) AS CNT
FROM CLUSTER_DOCUMENT AS CD 
INNER JOIN SESSION_DOCUMENT AS SD 
ON CD.DOCUMENT_ID = SD.DOCUMENT_ID
GROUP BY SD.SESSION_ID,CD.CLUSTER_ID) CNT2
ON B.SESSION_ID = CNT2.SESSION_ID
AND B.MAX_CNT = CNT2.CNT
GROUP BY B.SESSION_ID
...