postgresql 选择一行,如果不存在,则选择另一行

qojgxg4l  于 2024-01-07  发布在  PostgreSQL
关注(0)|答案(3)|浏览(144)

情况是这样的。
假设有一个包含几列的表:
| ID|名称|语言|......这是什么?|
| --|--|--|--|
| 1 |鲍勃|ES|......这是什么?|
| 2 |鲍勃|EN|......这是什么?|
| 3 |鲍勃|DE|......这是什么?|
| 4 |法案|EN|......这是什么?|
| 5 |杰克|EN|......这是什么?|
| 6 |杰克|DE|......这是什么?|
| 7 |杰克|ES|......这是一个问题。|
给定一种特定的语言,我想选择所有的ID。如果语言不可用,我想默认为EN。因此,作为一个示例,我为ES选择,我想得到:
| ID|名称|语言|......这是什么?|
| --|--|--|--|
| 1 |鲍勃|ES|......这是什么?|
| 4 |法案|EN|......这是什么?|
| 7 |杰克|ES|......这是什么?|
如果我选择EN,我希望得到:
| ID|名称|语言|......这是什么?|
| --|--|--|--|
| 2 |鲍勃|EN|......这是什么?|
| 4 |法案|EN|......这是什么?|
| 5 |杰克|EN|......这是什么?|
最多可以有10种不同的语言,总共有近50万行。大约有9万个不同的名字。
现在我找到了一个使用COALESCE的选项,但是这需要对每一行进行2次查询。
但是有没有更有效的方法来做到这一点呢?比如:SELECT..... WHERE language =('ES' AND IF NOT EXIST THEN 'EN)。
谢谢.

**Edit:**COALESCE方法不起作用。首先,它只能返回一列,其次,它只能返回一行。

oo7oh9g9

oo7oh9g91#

您可以使用DISTINCT ON:

SELECT DISTINCT ON (name)
    id
    , name
    , language
 FROM t1
 ORDER BY name
        , language = 'ES' DESC -- this one is your preference
        , language = 'EN' DESC -- default
        , id;

字符串

rhfm7lfc

rhfm7lfc2#

**编辑:**我错过了PostgreSQL标签--原来的答案是在MS SQL中。我下载了PostgreSQL并改编了它。

通常,我联接到一个用户表,并在此响应中模拟了这一点。
获取所有用户,将他们连接到语言表,然后在缺少所需语言的情况下使用case语句进行回退。

-- Demo Data
CREATE LOCAL TEMP TABLE tmp_Users (Name varchar(50));
INSERT INTO tmp_Users
VALUES 
    ('Bob'),
    ('Bill'),
    ('Jack');

CREATE LOCAL TEMP TABLE tmp_langs (Id int, Name varchar(50), Language varchar(2));
INSERT INTO tmp_langs
VALUES
    (1, 'Bob', 'ES'),
    (2, 'Bob', 'EN'),
    (3, 'Bob', 'DE'),
    (4, 'Bill', 'EN'),
    (5, 'Jack', 'EN'),
    (6, 'Jack', 'DE'),  
    (7, 'Jack', 'ES');

    
SELECT 
    u.Name,
    CASE
        WHEN sl.Language IS NOT null THEN sl.Language
        ELSE dl.Language -- this assumes every entry has an EN default
    END use_language
FROM tmp_Users u
LEFT JOIN tmp_langs AS sl --selectedLanguage
    on sl.Language = 'ES' AND u.Name = sl.Name -- join on your user ids
LEFT JOIN tmp_langs AS dl --defaultLanguage
    on dl.Language = 'EN' AND u.Name = dl.Name;

字符串
与ES
| 名称|使用语言|
| --|--|
| 鲍勃|ES|
| 法案|EN|
| 杰克|ES|
与EN
| 名称|使用语言|
| --|--|
| 鲍勃|EN|
| 法案|EN|
| 杰克|EN|
==就像我在你的问题中看到的那样==
在这里,您获取每个人的默认值,然后返回并将每个人与没有选定语言的EN默认值联合起来。

SELECT
    l.Id,
    l.Name,
    l.Language
FROM tmp_langs l
WHERE l.Language = 'EN'
UNION
SELECT
    dl.Id,
    dl.Name,
    dl.Language
FROM tmp_langs dl -- default language
WHERE dl.Language = 'EN'
  AND NOT EXISTS(SELECT 1 FROM tmp_langs l WHERE l.Language = 'ES' AND l.Name = dl.Name )
ORDER BY Id;

vcudknz3

vcudknz33#

主要答案

为了模拟/复制问题,我创建了以下临时表:

CREATE TEMP TABLE langs (id INT, name VARCHAR(25), lang CHAR(2));
INSERT INTO langs
VALUES
    (1, 'Bob', 'ES'),
    (2, 'Bob', 'EN'),
    (3, 'Bob', 'DE'),
    (4, 'Bill', 'EN'),
    (5, 'Jack', 'EN'),
    (6, 'Jack', 'DE'),  
    (7, 'Jack', 'ES');

CREATE TEMP TABLE users (name VARCHAR(25));
INSERT INTO users
VALUES 
    ('Bob'),
    ('Bill'),
    ('Jack');

字符串
我们至少执行两次过滤操作:一次针对所选语言,一次针对默认语言。因此,我认为创建一个函数而不是一遍又一遍地重复查询是有意义的:

CREATE OR REPLACE FUNCTION filter_by_lang(selected_lang CHAR(2))
RETURNS SETOF langs AS $$
    SELECT * FROM langs WHERE lang = selected_lang
$$ LANGUAGE SQL;

SELECT
    COALESCE(s.id, d.id) AS id,
    u.name,
    COALESCE(s.lang, d.lang) AS lang
FROM users AS u
    LEFT JOIN filter_by_lang('ES') AS s ON u.name = s.name
    LEFT JOIN filter_by_lang('EN') AS d ON u.name = d.name;


COALESCE返回第一个非NULL值。在我们的问题中,它返回特定用户的第一个可用id和语言。

奖金

假设有两种默认语言。因此,如果此人不会说所选语言或第一种默认语言,则显示第二种默认语言。使用相同的方法:

CREATE OR REPLACE FUNCTION filter_by_lang(selected_lang CHAR(2))
RETURNS SETOF langs AS $$
    SELECT * FROM langs WHERE lang = selected_lang
$$ LANGUAGE SQL;

SELECT
    COALESCE(s.id, d1.id, d2.id) AS id,
    u.name,
    COALESCE(s.lang, d1.lang, d2.lang) AS lang
FROM users AS u
    LEFT JOIN filter_by_lang('ES') AS s ON u.name = s.name
    LEFT JOIN filter_by_lang('EN') AS d1 ON u.name = d1.name
    LEFT JOIN filter_by_lang('DE') AS d2 ON u.name = d2.name


通过创建一个函数,我们保存了我们自己的重复编写,我们使查询更具可读性。

相关问题