在单个SELECT查询中查找最大值的总和

Ale*_*ber 5 sql postgresql plpgsql postgresql-9.5

我为我的问题准备了一个SQL小提琴.

在一个文字游戏中,我运行一个自定义函数来查找用户在她的最后一个回合中播放的所有单词:

应用截图

无效的话有一个scoreNULL(可能被更改为-1如果需要的话).

有效单词有正数score,如果有几个匹配单词,那么我只需要最高分(并丢弃其他分数).

例如,如果玩家玩得分为8的水平单词"ab"和得分为2的垂直单词"ab",则该回合只得到8分.

这是我的测试表:

CREATE TABLE words(word varchar, score integer);
Run Code Online (Sandbox Code Playgroud)

在这里,我填写测试数据:

INSERT INTO words (word, score) VALUES
('ab', 8),  -- word with higher score should be taken
('ab', 2),  -- same word with lower score should be discarded
('xy', 2),
('zz', NULL); -- invalid word marked by NULL (or -1)
Run Code Online (Sandbox Code Playgroud)

我可以看到是否播放了无效的单词

IF EXISTS (SELECT 1 FROM words WHERE score IS NULL) THEN
    RAISE EXCEPTION 'Invalid word played';
END IF;
Run Code Online (Sandbox Code Playgroud)

我可以删除重复的单词GROUP BY:

SELECT word, max(score) as score FROM words GROUP BY word;
Run Code Online (Sandbox Code Playgroud)

但问题是:

如何将上面的两个语句组合到一个SELECT语句中,以便我:

  1. 知道是否播放了无效的单词
  2. 比赛总和的总和(以便我可以更新球员的得分)

我正在寻找单个语句,因此自定义函数不会运行多次,最好没有临时表.

结果应该看起来像(我将从另一个自定义PL/pgSQL函数调用它):

DECLARE
    total_user_score    integer;
    invalid_words_found boolean;

SELECT
    .....,              -- how to calculate this value please?
    .....               -- how to calculate this value please?
INTO STRICT
    total_user_score,
    invalid_words_found
FROM words_check_words(....);  -- avoid calling this function twice

IF invalid_words_found THEN
    RAISE EXCEPTION "Invalid words found";
ELSE
    UPDATE games SET user_score = user_score + total_user_score;
END IF;
Run Code Online (Sandbox Code Playgroud)

sst*_*tan 1

(编辑为返回布尔值invalid_words_found
(编辑为使用bool_or聚合函数)

如果我理解正确的话:

with cte as (
    select max(score) as score,
           bool_or(score is null) as has_invalid
      from words_check_words(....)
     group by word
)
select coalesce(sum(score), 0) as total_user_score,
       bool_or(has_invalid) as invalid_words_found
  from cte
Run Code Online (Sandbox Code Playgroud)

您的自定义函数只会被调用一次。

编辑:集成到您的程序中,它看起来像这样:

DECLARE
    total_user_score    integer;
    invalid_words_found boolean;

with cte as (
    select max(score) as score,
           bool_or(score is null) as has_invalid
      from words_check_words(....)
     group by word
)
select coalesce(sum(score), 0),
       bool_or(has_invalid)
INTO STRICT
    total_user_score,
    invalid_words_found
FROM cte;

IF invalid_words_found THEN
    RAISE EXCEPTION "Invalid words found";
ELSE
    UPDATE games SET user_score = user_score + total_user_score;
END IF;
Run Code Online (Sandbox Code Playgroud)