2017-05-07 78 views
5

我有一個products表產品名稱不同的語言:如何在Mysql中爲GROUP BY定義自己的聚合函數?

product-id | lang-no | name 

我想列出每種產品一次,但使用不同語言的名稱。

我沒有所有產品名稱的所有語言,所以有時我必須回到另一種語言。

要挑選具有最低或最高數目的語言,我用

SELECT * FROM products JOIN 
(SELECT product-id, MIN(lang-no) AS minlang FROM products GROUP BY product-id) 
AS u ON products.product-id = u.product-id AND products.lang-no=minlang 

但現在我需要定義其他聚合函數,而不是最小或最大,所以我可以喜歡郎3號爲例。

如何在Mysql中定義我自己的聚合函數,例如:一些IF邏輯?

+0

如果'產品'可以有多種語言,並且您希望我們使用* *不同*語言,那麼所選語言應該與哪些不同? – toonice

+0

@toonice:當我用西班牙文名稱列出一個列表時,最好在列表中使用英文或其他任何名稱,而不是完全放棄該產品。現在,我沒有對後備語言的偏好。任何事情都可以。 –

回答

1

如果希望每個產品出現一次,再一個方法是使用變量:

select p.* 
from (select p.*, 
      (@rn := if(@pid = p.product_id, @rn + 1, 
         if(@pid := p.product_id, 1, 1) 
         ) 
      ) as rn 
     from products p cross join 
      (select @pid := -1, @rn := 0) params 
     order by product_id, field(lang_no, 3, 4, 1, 5, 2) -- or whatever 
    ) p 
where rn = 1; 

另一種方法是使用相關子查詢:

select p.* 
from products p 
where p.lang_no = (select p2.lang_no 
        from products p2 
        where p2.product_id = p.product_id 
        order by field(lang_no, 3, 4, 1, 5, 2) -- or whatever 
        limit 1 
       ); 

這兩個版本使用field()。這使您可以列出所有語言的優先級。

就你而言,相關的子查詢可能會更快,假設你有一個索引product_id

一個需要注意的是,所有的語言應該上市,因爲缺失值獲得一個0。如果這是一個問題,用這個邏輯:

field(lang_no, 1, 3) desc 

這將會把3作爲第一要務,1作爲第二個,然後是其他任何東西。

+0

我喜歡使用field()的相關子查詢和語言優先級。謝謝! –

3

您可以使用case與聚集指定(如果存在)返回,如果不分鐘(或最大)值值:

select p.* 
from products p 
join (
    select product_id, 
     case 
      when sum(lang_no = 3) > 0 
       then 3 
      else min(lang_no) 
      end as min_lang_no 
    from products 
    group by product_id 
    ) p2 on p.product_id = p2.product_id 
    and p.lang_no = p2.min_lang_no 
+1

我喜歡CASE的想法,但CASE中的SUM()是否正確?如果我只有語言代碼4和5的名稱,那麼該產品將跳過您的查詢... –

+2

@GeneVincent - 是的,它在MySQL上下文中是正確的。在其他DBMS中,您需要在SUM中使用一個案例。像' - SUM(當lang_no = 3時,1 else 0結束時)' – GurV

+1

'當max(lang_no = 3)'和'當bit_and(lang_no = 3)'也可以工作。 –

0

你的做法是錯誤的,違反了數據庫結構&規範化規則。

由於許多語言都有很多產品,所以我建議您爲產品和語言創建兩個單獨的表格,並通過標識表將它們鏈接起來。

所以我推薦:

Product: 
ProductId ProductName 

Language: 
LanguageId LanguageName 

Product_Lanugage: 
ProductId Languageid 

查詢會是這樣的:

SELECT * FROM products p INNER JOIN 
Product_language pl on p.productid=pl.prouctid 
INNER JOIN language l on l.lanugageid=pl.languageid 
where l.languagename like %(anylanuage)% 
1

下會選擇首選語言的name。如果名稱不適用於首選語言,那麼它將選擇具有最大值lang_no的任何語言。

SELECT product_id, 
     langNum, 
     name 
FROM (SELECT products.product_id AS product_id, 
       CASE 
        WHEN hasPreferredLang = 0 THEN 
         maxLangNum 
        ELSE 
         preferredLang 
       END AS langNum 
     FROM (SELECT product_id AS product_id, 
        MAX(lang_no) AS maxLangNum 
       FROM products 
       GROUP BY product_id 
      ) AS maxLangNumFinder 
     JOIN (SELECT product_id AS product_id, 
        SUM(CASE 
           WHEN lang_no = preferredLang THEN 
            1 
           ELSE 
            0 
          END) AS hasPreferredLang 
       FROM products 
       GROUP BY product_id 
      ) AS hasPreferredLangFinder ON hasPreferredLang.product_id = maxLangNumFinder.product_id 
    ) AS preferredLangNumFinder 
JOIN products ON preferredLangNumFinder.langNum = products.lang_no 
       preferredLangNumFinder.product_id = products.product_id; 

的語句開始通過確定什麼lang_no最大可用值是的product_id每個值。這是因爲我們可以確定lang_no的值用於product_id沒有首選語言條目的地方。

此查詢是然後INNER JOIN編到另一個列出每個product_id0值一起,以指示在product_id不具有與它或1其中不相關聯的優選的語言。

子查詢的結果然後用於測試每個product_id是否具有首選語言。如果是,則返回首選語言。如果不是,則使用lang_no的最大可用值。

所得product_id值和它們的選擇lang_no值列表然後INNER JOIN編到products上的product_idlang_no共享值,與product_id,選擇lang_noname爲所選擇的語言是從所得到的數據集SELECT編輯。

如果您有任何問題或意見,請隨時發佈相應評論。

1
SELECT p1.* 
FROM products p1 
WHERE p1.lang_no = (
    SELECT p2.lang_no 
    FROM products p2 
    WHERE p2.product_id = p1.product_id 
    ORDER BY p2.lang_no = 3 DESC, p2.lang_no ASC 
    LIMIT 1 
); 

如果存在對產品或至少long_no否則這lang_no相關子查詢將返回3