2012-02-16 71 views
6

Tôi có một hệ thống theo dõi xem tài liệu nào người dùng xem. Mỗi tài liệu có ID của nó và một cụm mà nó thuộc về. Hệ thống của tôi theo dõi ID phiên và số lượt xem. Bây giờ tôi muốn xây dựng một truy vấn SQL sẽ cung cấp cho tôi hai cột - ID phiên và cụm được phân loại. Thuật toán phân loại rất đơn giản:Phân loại 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 

Bảng cấu trúc như sau, tôi đang sử dụng MySQL 5.5.16:

phiên

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

SessionDocument

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

Cụm

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

ClusterDocument

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

Vì vậy, về cơ bản, tôi muốn chọn các cụm cho mỗi phiên, đếm sự xuất hiện của mỗi cụm cho các tài liệu đã xem và thấy sự xuất hiện tối đa. Sau đó, ID của cụm đã xảy ra nhiều nhất, là kết quả cho phiên làm việc do đó tập kết quả cuối cùng giữ session ID và xảy ra cụm nhất:

quả

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

tôi quản lý để có được những cụm các tài liệu đã xem cho mỗi phiên (bước 2/II.) với truy vấn này:

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) 

Tôi đang gặp khó khăn trong việc tìm phần còn lại. Điều này thậm chí có thể với các truy vấn SELECT lồng nhau không? Tôi có nên sử dụng con trỏ hay không và nếu có, ai đó có thể hiển thị ví dụ bằng con trỏ không? Bất kỳ trợ giúp sẽ được nhiều đánh giá cao.

EDIT # 1: thêm một C# thực hiện, MySQL đổ và kết quả dự kiến ​​

C# thực hiện

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 
     } 
    } 

Bảng cấu trúc, dữ liệu thử nghiệm và kết quả dự kiến ​​

Table structure and test data

Expected result

EDIT # 2: thêm một dữ liệu nhỏ hơn thiết lập và thuật toán thêm hương

Dưới đây là một tập hợp dữ liệu nhỏ hơn:

PHIÊN

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

CỤM

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 

Thuật toán một cách chi tiết

Bước 1: được cụm cho các tài liệu xem bởi phiên

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 

Bước 2: đếm xảy ra cụm

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 

Bước 3 (kết quả cuối cùng): tìm tối đa xảy ra cụm cho mỗi phiên (xem ở trên) và xây dựng tập kết quả cuối cùng (session_id, cluster_id):

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

CHỈNH SỬA # 3: Làm rõ câu trả lời được chấp nhận

Cả hai câu trả lời đều là chính xác. Cả hai đều cung cấp một giải pháp cho vấn đề. Tôi đã cho Mosty Mostacho câu trả lời được chấp nhận bởi vì trước tiên anh ấy đã đưa ra giải pháp và cung cấp một phiên bản khác của giải pháp với VIEW. Các giải pháp từ mankuTimma có chất lượng tương tự như Mosty Mostacho của giải pháp. Vì vậy, chúng tôi có hai giải pháp tốt như nhau, tôi chỉ chọn Mosty Mostacho vì anh ấy là người đầu tiên.

Cảm ơn cả hai vì những đóng góp của họ. .

+0

Nếu bạn có thể cung cấp dữ liệu mẫu cho bảng hiện tại và kết quả mong đợi từ dữ liệu mẫu của bạn, chúng tôi sẽ dễ dàng hơn cho chúng tôi –

+0

@MostyMostacho: Tôi đã thêm một số dữ liệu mẫu và kết quả mong đợi. – brozo

+2

+1 cho câu hỏi hay viết – YXD

Trả lời

2

Vâng, tôi có một số nghi ngờ về làm thế nào để chọn một xảy ra khi có nhiều bằng nhau, nhưng nhìn vào đoạn code C# có vẻ như lựa chọn này là không xác định.

Bây giờ, do dữ liệu sampla Bước 2 thực sự dẫn đến:

+------------+------------+-------+------------+ 
| 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 | 
+------------+------------+-------+------------+ 

Vì vậy, tiếp tục với các dữ liệu này, tôi nhận được session_id và max (cluster_id) cho rằng phiên id, kết quả là:

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

Tối đa (cluster_id) chỉ ở đó để thực hiện lựa chọn không xác định đó. Đây là truy vấn:

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 

Có lẽ thêm một cái nhìn sẽ cải thiện hiệu suất (thay thế của truy vấn ở trên):

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 

Hãy cho tôi biết nếu nó hoạt động.

+1

Giải pháp này hiện hoạt động. Cả hai - phiên bản SELECT lồng nhau và phiên bản VIEW. Thời gian cho cả hai trên một tập dữ liệu lớn là 0,686, điều này khá ấn tượng, vì vậy giải pháp VIEW ở cấp độ này không mang lại bất kỳ cải thiện nào về hiệu suất. Tôi sẽ đánh dấu đây là câu trả lời được chấp nhận vì bạn là người đầu tiên và cung cấp một phiên bản tối ưu của truy vấn. – brozo

2

Nếu tôi hiểu vấn đề của bạn chính xác, đối với mỗi phiên bạn muốn cụm có số lượt xem tài liệu nhiều nhất. Vì vậy, ở đây bạn đi Truy vấn dưới đây trả về số lượng tối đa hoặc số lần xuất hiện của một id cụm cụ thể cho mỗi id phiên.

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 

Sau đó, tham gia kết quả ở trên với truy vấn phụ (nơi tôi tính số) một lần nữa để nhận id cụm của lần xuất hiện tối đa. Trong trường hợp có hai cụm id với cùng số lần xuất hiện tôi đang sử dụng id cụm với giá trị tối đa. Tôi đã thử nghiệm dữ liệu của bạn và nó hoạt động. Ngoài ra, bây giờ truy vấn này sẽ hoạt động trên tất cả các cơ sở dữ liệu.

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 
+0

Kết quả của truy vấn này không khớp với kết quả mong đợi mà triển khai C# của tôi trả về. Nhưng tôi nghĩ nó khá gần. – brozo

+0

Có vẻ như truy vấn của tôi được sử dụng để cung cấp cho bạn số lượng tối thiểu thay vì số lượng tối đa. Chỉ có một chút lười biếng và không kiểm tra mã của tôi tốt.Tôi đã thay đổi nó. Hy vọng rằng nó sẽ giúp đỡ. – mankuTimma

+0

Truy vấn đã chỉnh sửa của bạn hoạt động rất tốt - 0.686 trên bộ dữ liệu lớn. Khá ấn tượng. – brozo