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

时间:2016-08-11 12:59:15

标签: sql postgresql plpgsql postgresql-9.5

我已为我的问题准备了an SQL Fiddle

在文字游戏中,我运行a custom function查找用户在上一回合播放的所有单词:

app screenshot

无效字词的scoreNULL(如果需要,可以更改为-1。)

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

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

这是我的测试表:

CREATE TABLE words(word varchar, score integer);

在这里我填写测试数据:

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)

我可以看到

是否播放了无效的字词
IF EXISTS (SELECT 1 FROM words WHERE score IS NULL) THEN
    RAISE EXCEPTION 'Invalid word played';
END IF;

我可以使用GROUP BY删除重复的字词:

SELECT word, max(score) as score FROM words GROUP BY word;

然而我的问题是:

如何将上述两个语句组合到单个SELECT语句中,以便I:

  1. 知道是否播放了无效字
  2. 比赛总分的总和(以便我可以更新球员的得分)
  3. 我正在寻找单个语句,因此the custom function不会多次运行,最好没有临时表。

    结果应该是这样的(我将从另一个自定义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;
    

3 个答案:

答案 0 :(得分: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

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

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

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;

答案 1 :(得分:1)

你可以这样做:

select   coalesce(sum(max_score), 0) as total_user_score,
         count(*) - count(max_score) as invalid_words_found
from     (select   max(score) as max_score
          from     words
          group by word) x

请参阅:Sql Fiddle

这假定如果一个单词无效,它也不会出现(在另一个记录中)具有非空分数。

IF语句需要如下所示:

IF invalid_words_found > 0 THEN

...您可以在错误消息中显示无效字的数量:

    RAISE EXCEPTION '% Invalid words found!', invalid_words_found;

答案 2 :(得分:1)

select 
  word,
  max(
    case 
      when score is null then raise_error('Invalid word: ' || word)::int 
      else score 
    end)
from words
group by word;

raise_error函数声明为

create function raise_error(text) returns text language plpgsql volatile as
$body$
begin
  raise exception '%', $1;
end $body$;

为了使查询更加优雅,可以创建另一个通用功能:

create function assert(
  p_cond boolean,
  p_message text,
  p_result anyelement) returns anyelement language plpgsql volatile as
$body$
begin
  if p_cond then
    return p_result;
  else
    raise exception '%', p_message;
  end if;
end $body$;

查询变得更加紧凑:

select 
  word,
  max(assert(score is not null, 'Invalid word: ' || word, score))
from words
group by word;