在单个 SELECT 查询中查找最大值的总和
Find sum of max values in a single SELECT query
我已经准备好 an SQL Fiddle 我的问题了。
在文字游戏中,我 运行 a custom function 查找用户在她上一回合玩过的所有单词:
无效单词的 score
为 NULL
(如果需要,可以更改为 -1
)。
有效词有正数score
,如果有多个匹配词,那么我只需要最高分(并丢弃其他分数)。
例如,如果玩家玩水平单词 "ab" 得分 8 和垂直单词 "ab" 得分 2,那么她在该回合只能获得 8 分。
这是我的测试table:
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
语句,以便我:
- 知道是否播放了无效单词
- 总得分总和(以便我可以更新玩家的得分)
我正在寻找一个单一的声明,这样 the custom function 就不会 运行 多次并且最好没有临时的 table.
结果应该如下所示(我将从另一个自定义 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;
(编辑为 return 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;
你可以这样做:
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;
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 '%', ;
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;
我已经准备好 an SQL Fiddle 我的问题了。
在文字游戏中,我 运行 a custom function 查找用户在她上一回合玩过的所有单词:
无效单词的 score
为 NULL
(如果需要,可以更改为 -1
)。
有效词有正数score
,如果有多个匹配词,那么我只需要最高分(并丢弃其他分数)。
例如,如果玩家玩水平单词 "ab" 得分 8 和垂直单词 "ab" 得分 2,那么她在该回合只能获得 8 分。
这是我的测试table:
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
语句,以便我:
- 知道是否播放了无效单词
- 总得分总和(以便我可以更新玩家的得分)
我正在寻找一个单一的声明,这样 the custom function 就不会 运行 多次并且最好没有临时的 table.
结果应该如下所示(我将从另一个自定义 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;
(编辑为 return 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;
你可以这样做:
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;
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 '%', ;
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;