I have prepared an SQL Fiddle for my question.
In a word game I run a custom function to find all words played by a user in her last turn:
Invalid words have a score
of NULL
(could be changed to -1
if needed).
Valid words have positive score
and if there are several matching words, then I only need the highest score (and discard the other score).
For example, if a player plays horizontal word "ab" with score 8 and vertical word "ab" with score 2, then she only gets 8 points for that turn.
Here is my test table:
CREATE TABLE words(word varchar, score integer);
And here I fill it with test data:
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)
I can see if an invalid word has been played by
IF EXISTS (SELECT 1 FROM words WHERE score IS NULL) THEN
RAISE EXCEPTION 'Invalid word played';
END IF;
And I can remove duplicate words with GROUP BY
:
SELECT word, max(score) as score FROM words GROUP BY word;
My question is however:
How to combine both statements above to a single SELECT
statement, so that I:
I am looking for a single statement, so that the custom function is not run several times and preferably without a temporary table.
The result should look like (I am going to call it from another custom PL/pgSQL function):
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;
(edited to return a boolean for invalid_words_found
)
(edited to use bool_or
aggregate function)
If I understood correctly:
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
Your custom function would only get called once.
EDIT: Integrating into your procedure, it would look something like this:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With