2017-05-07 33 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