从相关表中筛选按计数和特定值筛选

huangapple go评论59阅读模式
英文:

Filtering by count and specific value from a related table

问题

让我们假设我们有一个名为'grouper'的表格

id column_a
1 value
2 value

还有一个名为'translation'的表格,包含以下列

id locale text grouper_id
1 en some en text 1
2 es some es text 1
3 fr some fr text 1
4 en some en text 2

正如我们所看到的,'grouper'与'translation'存在一对多的关系。

我想要查询只有一个相关翻译且该翻译的语言环境是'en'的那些grouper(即,grouper的id是'2',因为它只有一个相关翻译,id是'4',并且这个翻译的语言环境是'en')。

我尝试做如下操作:

SELECT grouper.id, 
       grouper.column_a 
FROM grouper 
INNER JOIN translation 
        ON grouper.id = translation.grouper_id 
GROUP BY grouper.id 
HAVING COUNT(translation.id) = 1

返回的数据是正确的,但在这种情况下,没有考虑到语言环境。问题是,我不知道如何正确添加'locale'条件。

如果我将它添加为 WHERE translation.locale = 'en',结果将是不正确的,因为我将获取所有具有'en'语言环境的translations的grouper,而不管grouper有多少相关翻译。

如果我将条件添加到HAVING子句中,像这样 HAVING COUNT(translation.id) = 1 AND translation.locale = 'en',MySQL会抛出错误:

在'having clause'中未知列'translation.locale'。

非常感谢任何帮助。

英文:

Let's say we have a table called 'grouper'

id column_a
1 value
2 value

And a table called 'translation' that contains following columns

id locale text grouper_id
1 en some en text 1
2 es some es text 1
3 fr some fr text 1
4 en some en text 2

As we can see 'grouper' has one-to-many relation with 'translation'

What I want is to query only for those groupers that have only one related translation and that translation has to have 'en' locale. (i.e. grouper with id '2' because it has only one related translation with id '4' and this translation has 'en' locale).

I'm trying to do something like this:

SELECT gruper.id, 
       grouper.column_a 
FROM       grouper 
INNER JOIN translation 
        ON grouper.id = translation.grouper_id 
GROUP BY grouper.id 
HAVING COUNT(translation.id) = 1

And returned data is correct but locale is not considered in this case. The thing is, I don't know how to add the 'locale' condition correctly.

If I add it as WHERE translation.locale = 'en' the result will be incorrect since I will get all groupers with 'en' locale present in translations no matter how many related translations the grouper has.

And if I add the condition to HAVING clause like that HAVING COUNT(translation.id) = 1 AND translation.locale = 'en' MySQL throws an error:

>Unknown column 'translation.locale' in 'having clause'.

Any help is much appreciated.

答案1

得分: 1

在你的查询中存在两个问题:

  • "grouper.column_a" 既没有被聚合,也没有在 GROUP BY 子句中找到。最好将其移到 GROUP BY 子句内,以避免细微的错误。
  • "translation.locale" 在 HAVING 子句中没有被识别为字段,这是因为这个子句只在聚合已经完成后才被激活,因此 translation 不再存在。为了解决这个问题,你需要对相同的字段进行聚合,或者将其添加到 GROUP BY 子句中。前一种解决方案更好,因为第二种会导致不正确的结果(并且也没有意义)。
SELECT grouper.id, 
       grouper.column_a 
FROM       grouper 
INNER JOIN translation 
        ON grouper.id = translation.grouper_id 
GROUP BY grouper.id, grouper.column_a
HAVING COUNT(translation.id) = 1 AND MAX(translation.locale) = 'en'

如果你知道每个翻译在你的分区内不会重复,就像你分享的示例数据一样,你甚至可以通过检查翻译.locale = en 的总和是否为 1 来将条件减少到一个条件,如下所示:

HAVING SUM(CASE WHEN translation.locale = 'en' THEN 1 ELSE -1 END) = 1

这将确保当这个总和为 1 时,唯一的情况是翻译.locale = 'en',并且没有其他翻译(计数行 = 1)。

输出

id column_a
2 value

在此查看演示链接

进一步的参考资料:

英文:

There are two issues in your query:

  • "grouper.column_a" is neither aggregated, nor found within the GROUP BY clause. It's better to move it inside the GROUP BY clause to avoid subtle mistakes.
  • "translation.locale" is not recognized as a field inside the HAVING clause, that's because this clause is activated only after the aggregation has already been carried out, for this reason translation doesn't exist anymore. In order to solve this issue, you either need to aggregate the same field, or add it to the GROUP BY clause. The former solution is preferred, as the second one would lead to incorrect result (and would not make sense as well).
SELECT grouper.id, 
       grouper.column_a 
FROM       grouper 
INNER JOIN translation 
        ON grouper.id = translation.grouper_id 
GROUP BY grouper.id, grouper.column_a
HAVING COUNT(translation.id) = 1 AND MAX(translation.locale) = 'en'

If you know that each translation cannot be repeated within your partitions, as it seems from your shared sample data, you could even reduce conditions to one only, by checking when sum of translation.locale = en is 1 as follows:

HAVING SUM(CASE WHEN translation.locale = 'en' THEN 1 ELSE -1 END) = 1

This would ensure that the only case when this sum gets 1 is when there's one translation.locale = 'en' and no other translations (count rows = 1).

Output:

id column_a
2 value

Check the demo here.


Further references:

答案2

得分: 1

你可以这样表达:我想要那些最小和最大语言环境都是'en'的分组ID。

你可以使用连接(join)或INEXISTS子句来实现这个目标。我使用IN是因为它简单,并且我们只想从分组表中选择数据:

SELECT *
FROM grouper 
WHERE id IN
(
  SELECT grouper_id
  FROM translation 
  GROUP BY grouper_id
  HAVING MIN(locale) = 'en' AND MAX(locale) = 'en'
);
英文:

You can phrase it like this: I want those grouper IDs for which both the minimum and the maximum locale is 'en'.

You can do this with a join or with an IN or EXISTS clause. I am using IN for its simplicity and because we only want to select data from the grouper table:

SELECT *
FROM grouper 
WHERE id IN
(
  SELECT grouper_id
  FROM translation 
  GROUP BY grouper_id
  HAVING MIN(locale) = 'en' AND MAX(locale) = 'en'
);

答案3

得分: 0

你可以使用 inner join 来连接仅有一个翻译(应为 en)的一组组别。

select g.*
from grouper g
inner join (
  select grouper_id
  from translation
  group by grouper_id
  having count(*) = 1 and sum(locale='en') = 1 
) as s on s.grouper_id = g.id
英文:

You can use inner join to join a list of groupers that only have one translation, which should be en.

select g.*
from grouper g
inner join (
  select grouper_id
  from translation
  group by grouper_id
  having count(*) = 1 and sum(locale='en') = 1 
) as s on s.grouper_id = g.id

huangapple
  • 本文由 发表于 2023年5月28日 18:57:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76351125.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定