选择仅匹配一个OR条件的行

时间:2014-06-29 08:36:39

标签: sql postgresql

将这样的表视为一个非常简单的例子:

|----------------------------------|
|              names               |
|----------------------------------|
| id | name_id |  name  | language |
|----------------------------------|
| 1  |    1    |  foo   |    en    |
|----------------------------------|
| 2  |    1    | foose  |    fr    |
|----------------------------------|

我希望得到名称为'foo'且语言为'fr'的行,但如果没有语言'fr'的行,则回退到语言为'en'的行。所以我的第一个想法就是写一个这样的查询:

SELECT * 
FROM names 
WHERE name ILIKE '%foo%' AND (language = 'fr' OR language = 'en')

只要只有一种语言可用,这样就可以正常工作,但当两种语言都可用时,它会返回两行,所以我添加了一个DISTINCT ON:

SELECT DISTINCT ON (name_id) * 
FROM names 
WHERE name ILIKE '%foo%' AND (language = 'fr' OR language = 'en')

但是这个不起作用,因为它返回语言为'en'的那个。

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

就像你用你的第一个查询做的那样,只是选择排序的前一个查询" fr"来之前" en"。

SELECT * 
FROM names 
WHERE name LIKE '%foo%' AND (language = 'fr' OR language = 'en')
ORDER BY language DESC
LIMIT 1

答案 1 :(得分:1)

with s as (
    select *
    from names 
    where
        name ilike '%foo%'
        and
        language in ('fr', 'en')
), fr as (
    select *
    from s
    where language = 'fr'
), en as (
    select *
    from s
    where language = 'en'
)
select *
from fr
union all
select *
from en
where (select count(*) from fr) = 0

如果法语行数为零,它只会添加英文行。

答案 2 :(得分:0)

这是我最后的实施,它依赖于@Amadan的评论,所以对他赞不绝口。 @Clodoaldo Neto方法也适用,但这个对我来说更简单。

SELECT DISTINCT ON (name_id) * 
FROM names
WHERE name ILIKE '%foo%' AND language IN ('en', 'fr')
ORDER BY name_id, language = 'fr' DESC

最后一个提示,如果语言可以为空(就像在我的情况下一样),条件应该合并,因为它将返回置于顶部的空值:

SELECT DISTINCT ON (name_id) * 
FROM names
WHERE name ILIKE '%foo%' AND language IN ('en', 'fr')
ORDER BY name_id, COALESCE(language = 'fr', FALSE) DESC