2017-05-07 2 views
5

Ich habe eine products Tabelle mit Produktnamen in verschiedenen Sprachen:Wie definiert man eigene Aggregatfunktion in Mysql für GROUP BY?

product-id | lang-no | name 

ich jedes Produkt einmal auflisten möchten, aber eine andere Sprache Namen.

Ich habe nicht alle Sprachen für alle Produktnamen, daher muss ich manchmal auf eine andere Sprache zurückgreifen. Ich kann so bevorzugen lang-NO3 zum Beispiel

Um die Sprache mit der niedrigsten oder höchsten Zahl nehme ich verwenden

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 

Aber jetzt brauche ich eine andere Aggregatfunktion anstelle von MIN oder MAX zu definieren.

Wie definiere ich meine eigene Aggregatfunktion in Mysql, z. einige IF Logik?

+0

Wenn ein 'Produkt' viele Sprachen haben kann und Sie möchten, dass wir eine * andere * Sprache verwenden, von welcher Sprache sollte dann die gewählte Sprache abweichen? – toonice

+0

@toonice: Wenn ich eine Liste mit spanischen Namen mache, ist es besser, den englischen oder irgendeinen anderen Namen in der Liste zu verwenden, als das Produkt komplett auszulassen. Im Moment habe ich keine Vorliebe für die Fallback-Sprache. Alles ist in Ordnung. –

Antwort

1

Wenn Sie jedes Produkt wollen einmal erscheinen, dann ist eine Methode, Variablen zu verwenden:

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; 

Ein anderes Verfahren verwendet eine korrelierte Unterabfrage:

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 
       ); 

Beide Versionen field() verwenden. Auf diese Weise können Sie alle Sprachen mit ihrer Priorität auflisten.

In Ihrem Fall ist die korrelierte Unterabfrage wahrscheinlich schneller, vorausgesetzt, Sie haben einen Index auf product_id.

Die einzige Einschränkung ist, dass alle Sprachen aufgeführt werden sollte, weil die fehlenden Werte erhalten eine 0. Wenn das ein Problem ist, diese Logik verwenden:

field(lang_no, 1, 3) desc 

Dieses 3 als erste Priorität gesetzt wird, 1 als der zweite und dann noch etwas anderes.

+0

Ich mag die korrelierten Unterabfrage und Sprache Prioritäten mit field(). Vielen Dank! –

3

Sie können case mit Aggregaten verwenden, um welchen Wert zurück, wenn vorhanden ist und wenn nicht min (oder max) Wert angeben:

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

Ich mag die CASE-Idee, aber ist die SUM() im CASE wirklich richtig? Wenn ich nur Namen für die Sprachencodes 4 und 5 habe, würde das Produkt mit Ihrer Anfrage übersprungen werden ... –

+2

@GeneVincent - Ja, das stimmt im MySQL-Kontext. In anderen DBMS müssen Sie einen Fall innerhalb von SUM verwenden. Wie - 'SUM (fall wenn lang_no = 3 dann 1 else 0 end)' – GurV

+1

'wenn max (lang_no = 3)' und 'wenn bit_and (lang_no = 3)' würde auch funktionieren. –

0

Ihr Ansatz ist falsch und Verletzung der db Struktur & Normalisierungsregeln.

Da viele Sprachen viele Produkte haben, empfehle ich Ihnen, zwei separate Tabellen für Produkt und Sprache zu erstellen und sie über eine Identitätstabelle zu verknüpfen.

so empfehlen I:

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

Im Folgenden wird wählen in der bevorzugten Sprache des name:

Product: 
ProductId ProductName 

Language: 
LanguageId LanguageName 

Product_Lanugage: 
ProductId Languageid 

Die Abfrage so sein würde. Wenn der Name nicht in der bevorzugten Sprache verfügbar ist, wählt er die Sprache mit dem höchsten Wert 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; 

Die Anweisung beginnt mit der Bestimmung, was der maximal verfügbare Wert von lang_no für jeden Wert von product_id ist. Dies ist so, dass wir einen Wert von lang_no bestimmen können, um zu verwenden, wo der product_id keinen Eintrag für die bevorzugte Sprache hat.

Diese Unterabfrage ist dann INNER JOIN ed auf eine andere, die jeden product_id zusammen mit einem Wert von 0 auflistet, um anzuzeigen, wenn der product_id nicht die bevorzugte Sprache mit ihm verbunden hat oder 1 wo es der Fall ist.

Die Ergebnisse der Unterabfrage werden dann verwendet, um zu testen, ob jede product_id die bevorzugte Sprache hat. Wenn dies der Fall ist, wird die bevorzugte Sprache zurückgegeben. Wenn dies nicht der Fall ist, wird der größte verfügbare Wert von lang_no verwendet.

Die sich ergebende Liste von product_id Werte und ihre gewählten lang_no Werte ist dann INNER JOIN ED products auf die gemeinsamen Werte der product_id und lang_no, mit dem product_id, ausgewählt lang_no und name für die gewählte Sprache SELECT ED aus dem resultierenden Datensatzes sein.

Wenn Sie irgendwelche Fragen oder Kommentare haben, dann zögern Sie nicht, einen Kommentar entsprechend zu posten.

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 
); 

Die korrelierte Unterabfrage wird 3 zurück, wenn diese lang_no für das Produkt vorhanden ist oder die am wenigsten long_no anders.