2012-12-14 59 views
1

有2個引用(user_iditem_id),我需要查詢找到與某些項目的所有用戶一個表排序。 棘手的部分是,我需要根據結果排序結果,而不僅僅是結果的數量,而是基於它們的WHICH項目。從單一的表中返回結果由我多少結果

這裏的表:

+--------------+-----------------------+------+-----+---------+-------+ 
| Field  | Type     | Null | Key | Default | Extra | 
+--------------+-----------------------+------+-----+---------+-------+ 
| user_id  | int(11)    | NO |  | 0  |  | 
| item_id  | int(11) unsigned  | YES |  | NULL |  | 
+--------------+-----------------------+------+-----+---------+-------+ 

所以我的查詢如下所示:

SELECT user_id, item_id 
FROM  user_items 
WHERE item_id IN (2, 122, 132) 
GROUP BY user_id, item_id 
HAVING SUM(item_id = 2); 

看起來很容易嗎?這裏是艱難的部分進來:

item_id = 2是必需的 item_id = 122和132是可選的。 132之後的任何內容也是可選的。

我需要基於訂購的結果: 1)如果所有的項目被發現。 2)如果僅找到項目2和122。 3)如果只找到第2項。

下面是擺弄的SQL小提琴文件:http://sqlfiddle.com/#!2/6b1c1/6/0

我在想,如果有一些方法我可以設置的,這樣的事情:SELECT查詢說

IF (item_id = 2 AND item_id = 122 AND item_id = 132) AS matches_all, 
IF (item_id = 2, item_id = 122) AS matches_some, 
IF (item_id = 2) AS matches_first 

編輯與更新查詢 這是我迄今爲止。它是我所需要的約95%: http://sqlfiddle.com/#!2/6b1c1/47

SELECT user_id, item_id, 
    @tmp_1 := IF(SUM(item_id = 2), 1, 0) AS tmp_1, 
    @tmp_2 := IF(SUM(item_id = 122), 1, 0) AS tmp_2, 
    @tmp_3 := IF(SUM(item_id = 132), 1, 0) AS tmp_3, 
    @tmp_4 := IF(SUM(item_id = 126), 1, 0) AS tmp_4, 
    CAST(@tmp_3 + @tmp_4 AS UNSIGNED) AS total_other 
FROM  user_items 
WHERE item_id IN (2, 122, 132, 126) 
GROUP BY user_id 
HAVING SUM(item_id = 2) 
ORDER BY tmp_1 DESC, tmp_2 DESC, total_other DESC 

一對夫婦更多的細節:

1)我只會有一個最大的12個項目進入,所以我可以指定每一個它自己的溫度場如果需要的話。

2)上面的查詢完全適用於tmp_1和TMP_2。如果我們有一個擁有第2項和第122項的用戶,它將這些放在列表的頂部。 對於剩下的,3-4(3至最多12個),我需要匹配的數目,這就是爲什麼在我CAST(@tmp_3 + @tmp_4做出了嘗試的計算。我不知道如何讓這些計算。

3)一旦我有項目3的總計算 - 12,則這將是ORDER BY子句中的第三個和最後一個項目。

結果示例 基於在SQL小提琴文件中提供的模式,這裏要說的是,應根據搜索與ITEM_ID所有用戶返回的結果'S:2, 122, 132, 126

+---------+--------------+----------------+-------------+ 
| USER_ID | PRIMARY_ITEM | SECONDARY_ITEM | OTHER_ITEMS | 
+---------+--------------+----------------+-------------+ 
| 39  | 1   | 1    | 2   | 
| 54  | 1   | 1    | 0   | 
| 55  | 1   | 0    | 0   | 
+---------+--------------+----------------+-------------+ 
+1

您的查詢總廢話:'HAVING SUM(ITEM_ID = 2)'? – Bohemian

+2

老兄!如果你有更好的東西,請告訴我!我沒有發佈我的問題,因爲我的查詢是完美的。我正在嘗試做一些我無法弄清楚如何去做的事情。查看SQLFiddle鏈接以瞭解它是如何工作的。 'HAVING SUM(item_id = 2)'只是確保item_id 2存在於結果中。 –

+1

'HAVING SUM(item_id = 2)'不一定是「無稽之談」。這個特殊的表達式可能不會達到理想的結果集,但像這樣的表達式通常是有效的,有時是有用的,偶爾也是不可或缺的。 – spencer7593

回答

1

UPDATE:

基礎上更新您的問題(包括所期望的結果集),這裏是一個返回結果集的查詢。(這是非常類似於在聯視圖查詢說明在原來的答案)

SELECT i.user_id       AS user_id 
     , MAX(IF(i.item_id= 2 ,1,0))  AS primary_item 
     , MAX(IF(i.item_id= 122 ,1,0))  AS secondary_item 
     , MAX(IF(i.item_id= 132 ,1,0)) + 
     MAX(IF(i.item_id= 126 ,1,0))  AS other_items 
    FROM user_items i 
    WHERE i.item_id IN (2, 122, 132, 126) 
    GROUP BY i.user_id 
    HAVING primary_item 
    ORDER 
     BY primary_item DESC 
     , secondary_item DESC 
     , other_items DESC 
     , i.user_id 

注意,表達式來計算other_items柱可以擴展到處理任何數量的其他items_id值。 (你只是想確定在那裏沒有指定相同的item_id兩次,或者它將被「計數」兩次),例如,

 , MAX(IF(i.item_id= 132 ,1,0)) + 
     MAX(IF(i.item_id= 133 ,1,0)) + 
     MAX(IF(i.item_id= 135 ,1,0)) + 
     MAX(IF(i.item_id= 137 ,1,0)) + 
     MAX(IF(i.item_id= 143 ,1,0))  AS other_items 

這基本上做檢查對於每個項目,然後得出一個1或0,然後加入了1和0拿出總。

還要注意的是,IF()函數的調用是沒有必要的,這些表達實際上可以簡化爲:

 , MAX(i.item_id= 2)     AS primary_item 
     , MAX(i.item_id= 122)    AS secondary_item 

注意,WHERE條款實際上並不需要返回正確的結果集。 (但是如果它存在,謂詞必須與SELECT列表中正在檢查的item_id值相匹配

還要注意,ORDER BY不需要包含primary_item DESC,因爲我們的查詢保證值爲primary_item將是一個1。這是足以與secondary_item DESC啓動順序,因爲這可以是1或0。

覆蓋索引on (user_id,item_id)可以加速性能,或可能的item_id領先列的索引可能會更好。(缺少WHERE子句,查詢將需要檢查表中的每一行,基本上是全表掃描或全索引掃描。)

從結果集中看,如果用戶擁有一個或多個項目(而不是計算他擁有的特定項目的數量),則您想返回'1'。如果要返回的內容是每個項目的數量的計數,那麼你會用SUM()聚合來代替MAX()聚合,但是這對於解讀OTHER_ITEMS列的內容來說更成問題。

注意HAVING primary_item子句是讓我們只有行的那些用戶至少有一個item_id = 2


UPDATE:

弗朗西斯說......該查詢[在你原來的答案]將返回每個用戶多個結果,這是不是我後。

A:這是一個很好的例子,顯示你想返回的結果集的例子會有好處。您的查詢在SELECT列表中同時包含user_id和item_id`,並且沒有任何跡象表明您希望每個用戶只返回一行,或者每個user_id和item_id組合只返回一行。

爲了得到這個結果,只需在ORDER BY子句前添加一個GROUP BY d.user_idGROUP BY d.user_id, d.item_id子句。


這是不優雅,但我認爲這將返回指定的結果集。

SELECT d.user_id 
    , d.item_id 
    FROM user_items d 
    JOIN ( 
     SELECT i.user_id 
       , MAX(IF(i.item_id=2 ,1,0)) AS item_2 
       , MAX(IF(i.item_id=122,1,0)) AS item_122 
       , MAX(IF(i.item_id=132,1,0)) AS item_132 
      FROM user_items i 
      WHERE i.item_id IN (2, 122, 132) 
      GROUP BY i.user_id 
     HAVING item_2 
      ORDER BY 3 DESC, 4 DESC, 1 
     ) f 
    ON d.user_id = f.user_id 
WHERE d.item_id IN (2, 122, 132) 
ORDER BY (f.item_122 AND f.item_132) DESC 
     , f.item_122 DESC 
     , d.user_id 
     , d.item_id 

內聯視圖(別名爲f查詢)不「檢驗」該項目中的被發現的用戶。


要看到這是如何工作的,我們第一,我們檢查只是內嵌視圖的結果...

  SELECT i.user_id 
       , MAX(IF(i.item_id=2 ,1,0)) AS item_2 
       , MAX(IF(i.item_id=122,1,0)) AS item_122 
       , MAX(IF(i.item_id=132,1,0)) AS item_132 
      FROM user_items i 
      WHERE i.item_id IN (2, 122, 132) 
      GROUP BY i.user_id 
     HAVING item_2 
      ORDER BY 3 DESC, 4 DESC, 1 

WHERE條款可以被省略。對於我們這裏的目的,我們基本上只是獲取user_id的列表,以及它們具有哪些指定項目的指示符。

MAX聚合內部的表達式檢查item_id是否分別匹配2,122或132,並返回1或0.我們使用MAX聚合拉出我們找到的任何值1。

我們確實需要GROUP BY,所以我們得到一個明確的user_id列表。

我們使用HAVING子句,以便省略沒有item_id = 2的用戶。它可以被寫成這樣

  HAVING item_2 > 0 

(增加大於零越大,但不是必需的,因爲我們保證ITEM_2將有值爲0或1)

ORDER BY是不是真的(因爲我們打算將它加回到user_items表)(ORDER BY只在最外面的查詢中需要)。但它確實證明可以獲得此結果集的有序性。

(如果這是我的要求,我可能只是停在這裏,並利用此結果集的,但是這不是你所指定的結果集。)

我們加入一個查詢(使用它作爲內嵌視圖,或以MySQL的說法派生的表)到user_items表,所以我們只返回那些匹配那個查詢中的user_id的用戶。

我們需要添加WHERE子句,所以我們只在指定的列表中提取item_id值。

而且我們需要ORDER BY以指定的順序將結果集傳給我們。

+0

這並不完全是我之後的事情。我剛剛使用新的SQLFiddle文件更新了我的問題,這個文件在這一點上非常接近,所以您可以更好地瞭解我所追求的內容。看起來我只需要了解如何計算tmp字段。 –

+0

@Francis Lewis:我建議你非常小心的用戶變量,並確保你瞭解操作的順序。他們有時不像你期望的那樣工作。 – spencer7593

+0

@Francis Lewis:我不太清楚我提供的查詢的結果集不符合規範。 – spencer7593

0

它看起來像你所需要的是哪些字段是必需的,哪些字段是可選的規則或映射。如果你有某種數學規則說,我不知道,也許需要編號爲10,所有其他選項都是可選的,你可以在那裏使用某些條款。

假設item_id是完全隨機的,我建議您創建一個映射表,對您的項目進行排名/優先級排序。也許是這樣的item_rank表:

 
------------------------- 
| item_id | is_optional | 
------------------------- 
|  2 |   1 | 
------------------------- 
|  122 |   0 | 
------------------------- 
|  133 |   0 | 
------------------------- 

那麼你的查詢是:

SELECT user_map.user_id, user_map.item_id, 
FROM user_map 
INNER JOIN item_rank 
ON user_map.item_id = item_rank.item_id 
    AND user_map.item_id IN (2,122,133) 
GROUP BY user_map.user_id 
HAVING item_rank.is_optional > 0 
ORDER BY COUNT(user_map.item_id); 

我不正是這樣的解決方案,但是不知道多一點的你想在完成什麼一天結束時,我無法提供更具體的解決方案。

在附註中,當問題很難解決時,通常意味着你試圖以錯誤的方式來對待它們。當我發現自己處於架構綁定中時,當我從頭開始重新追蹤並思考時,我傾向於始終找到更清晰的解決方案。顯然取決於你有多遠,但可能是值得的。

祝你好運!

+0

映射表比我之前的要複雜一點,它不會考慮每個可能的用戶輸入,因爲它需要首先由包含所有結果的匹配來排序,然後通過包含前2個和最高的匹配其他結果的數量,然後通過包含第一個匹配加上其他匹配的最高數量的匹配。 –

+0

@FrancisLewis啊,我明白了。你能提供一些關於你想要做什麼的背景嗎?我的意思是更高的水平。像「我有一個項目列表,並希望看到用戶是否購買了某些項目來確定價格點」? – tazer84

0

好吧,這就是我想出來的。我只需要在第一個2之後計算任何項目,所以我想出了一個比使用臨時字段更清潔的解決方案,並且最終能夠實現一個更清晰的解決方案。

SELECT user_id, 
     IF(SUM(item_id = 2), 1, 0) AS primary_item, 
     IF(SUM(item_id = 122), 1, 0) AS secondary_item, 
     (IF(SUM(item_id = 132), 1, 0) + IF(SUM(item_id = 126), 1, 0)) AS other_items 
FROM  user_items 
WHERE item_id IN (2, 122, 132, 126) 
GROUP BY user_id 
HAVING SUM(item_id = 2) 
ORDER BY primary_item DESC, secondary_item DESC, other_items DESC 

所以這給了我一個領域的第一項,第二項,所以我可以看到,如果這些匹配,則計數所有的休息,這可以有多達10個其他項目。

然後它根據我們是否有第一個項目,第二個項目,然後是所有其他項目的總計數進行訂購。

你可以在這裏看到的最終結果是:http://sqlfiddle.com/#!2/6b1c1/131