В PostgreSQL 10.6 я пытаюсь сохранить результаты двух вызовов агрегатных функций в столбцах avg_score
и avg_time
таблицы words_users
, но, к сожалению, получаю синтаксическую ошибку:
WITH last_week_moves AS (
SELECT
m.gid,
m.uid,
m.played - LAG(m.played) OVER(PARTITION BY m.gid ORDER BY played) AS diff
FROM words_moves m
JOIN words_games g ON (m.gid = g.gid AND 5 IN (g.player1, g.player2))
WHERE m.played > CURRENT_TIMESTAMP - INTERVAL '1 week'
)
UPDATE words_users SET
avg_score = (SELECT ROUND(AVG(score), 1) FROM words_moves WHERE uid = 5),
avg_time = TO_CHAR(AVG(diff), 'HH24:MI')
FROM last_week_moves
WHERE uid = 5
GROUP BY uid;
(я использую жестко закодированный uid = 5
в вышеприведенном выражении, но в реальной жизни последний заключен в хранимую функцию PL / PgSQL и использует параметр uid = in_uid
).
ERROR: 42601: syntax error at or near "GROUP"
LINE 15: GROUP BY uid
^
LOCATION: scanner_yyerror, scan.l:1128
База данных, похоже, недовольна GROUP BY
, но мне она нужна для AVG(diff)
, потому что CTE обеспечивает время между ходами всегда для 2 игроков в игре:
SELECT
m.gid,
m.uid,
m.played - LAG(m.played) OVER(PARTITION BY m.gid ORDER BY played) AS diff
FROM words_moves m
JOIN words_games g ON (m.gid = g.gid AND 5 IN (g.player1, g.player2))
WHERE m.played > CURRENT_TIMESTAMP - INTERVAL '1 week';
gid | uid | diff
-------+-------+-----------------------
50399 | 774 | ¤
50608 | 8977 | ¤
50608 | 5 | 00:39:48.121149
50608 | 8977 | 00:09:46.221235
50608 | 5 | 01:35:23.524209
50608 | 8977 | 09:26:40.794061
50697 | 5 | ¤
50697 | 10322 | 02:13:16.502079
50697 | 5 | 01:47:44.681788
50697 | 10322 | 00:01:31.597973
50697 | 5 | 12:11:24.54716
50697 | 10322 | 12:01:15.078243
50697 | 5 | 11:52:39.60056
50697 | 10322 | 00:11:30.491137
50697 | 5 | 00:14:53.612513
50697 | 10322 | 01:45:23.940957
...
52469 | 5 | 02:46:29.768655
52469 | 8550 | 01:16:45.169882
52469 | 5 | 08:38:00.691552
(59 rows)
Кто-нибудь знает, пожалуйста, как изменить мой UPDATE
запрос?
Ниже приведены три таблицы:
# \d words_users
Table "public.words_users"
Column | Type | Collation | Nullable | Default
---------------+--------------------------+-----------+----------+------------------------------------------
uid | integer | | not null | nextval('words_users_uid_seq'::regclass)
created | timestamp with time zone | | not null |
visited | timestamp with time zone | | not null |
ip | inet | | not null |
fcm | text | | |
apns | text | | |
adm | text | | |
motto | text | | |
vip_until | timestamp with time zone | | |
grand_until | timestamp with time zone | | |
banned_until | timestamp with time zone | | |
banned_reason | text | | |
elo | integer | | not null |
medals | integer | | not null |
coins | integer | | not null |
avg_score | double precision | | |
avg_time | text | | |
Indexes:
"words_users_pkey" PRIMARY KEY, btree (uid)
Check constraints:
"words_users_banned_reason_check" CHECK (length(banned_reason) > 0)
"words_users_elo_check" CHECK (elo >= 0)
"words_users_medals_check" CHECK (medals >= 0)
Referenced by:
TABLE "words_chat" CONSTRAINT "words_chat_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
TABLE "words_games" CONSTRAINT "words_games_player1_fkey" FOREIGN KEY (player1) REFERENCES words_users(uid) ON DELETE CASCADE
TABLE "words_games" CONSTRAINT "words_games_player2_fkey" FOREIGN KEY (player2) REFERENCES words_users(uid) ON DELETE CASCADE
TABLE "words_moves" CONSTRAINT "words_moves_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
TABLE "words_reviews" CONSTRAINT "words_reviews_author_fkey" FOREIGN KEY (author) REFERENCES words_users(uid) ON DELETE CASCADE
TABLE "words_reviews" CONSTRAINT "words_reviews_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
TABLE "words_scores" CONSTRAINT "words_scores_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
TABLE "words_social" CONSTRAINT "words_social_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
TABLE "words_stats" CONSTRAINT "words_stats_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
# \d words_moves
Table "public.words_moves"
Column | Type | Collation | Nullable | Default
---------+--------------------------+-----------+----------+------------------------------------------
mid | bigint | | not null | nextval('words_moves_mid_seq'::regclass)
action | text | | not null |
gid | integer | | not null |
uid | integer | | not null |
played | timestamp with time zone | | not null |
tiles | jsonb | | |
score | integer | | |
letters | text | | |
hand | text | | |
puzzle | boolean | | not null | false
Indexes:
"words_moves_pkey" PRIMARY KEY, btree (mid)
"words_moves_gid_played_idx" btree (gid, played DESC)
"words_moves_uid_idx" btree (uid)
Check constraints:
"words_moves_score_check" CHECK (score >= 0)
Foreign-key constraints:
"words_moves_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
"words_moves_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
Referenced by:
TABLE "words_scores" CONSTRAINT "words_scores_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid) ON DELETE CASCADE
# \d words_games
Table "public.words_games"
Column | Type | Collation | Nullable | Default
----------+--------------------------+-----------+----------+------------------------------------------
gid | integer | | not null | nextval('words_games_gid_seq'::regclass)
created | timestamp with time zone | | not null |
finished | timestamp with time zone | | |
player1 | integer | | not null |
player2 | integer | | |
played1 | timestamp with time zone | | |
played2 | timestamp with time zone | | |
state1 | text | | |
state2 | text | | |
reason | text | | |
hint1 | text | | |
hint2 | text | | |
score1 | integer | | not null |
score2 | integer | | not null |
chat1 | integer | | not null |
chat2 | integer | | not null |
hand1 | character(1)[] | | not null |
hand2 | character(1)[] | | not null |
pile | character(1)[] | | not null |
letters | character(1)[] | | not null |
values | integer[] | | not null |
bid | integer | | not null |
friendly | boolean | | |
Indexes:
"words_games_pkey" PRIMARY KEY, btree (gid)
"words_games_player1_coalesce_idx" btree (player1, COALESCE(finished, 'infinity'::timestamp with time zone))
"words_games_player2_coalesce_idx" btree (player2, COALESCE(finished, 'infinity'::timestamp with time zone))
Check constraints:
"words_games_chat1_check" CHECK (chat1 >= 0)
"words_games_chat2_check" CHECK (chat2 >= 0)
"words_games_check" CHECK (player1 <> player2)
"words_games_score1_check" CHECK (score1 >= 0)
"words_games_score2_check" CHECK (score2 >= 0)
Foreign-key constraints:
"words_games_bid_fkey" FOREIGN KEY (bid) REFERENCES words_boards(bid) ON DELETE CASCADE
"words_games_player1_fkey" FOREIGN KEY (player1) REFERENCES words_users(uid) ON DELETE CASCADE
"words_games_player2_fkey" FOREIGN KEY (player2) REFERENCES words_users(uid) ON DELETE CASCADE
Referenced by:
TABLE "words_chat" CONSTRAINT "words_chat_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
TABLE "words_moves" CONSTRAINT "words_moves_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
TABLE "words_scores" CONSTRAINT "words_scores_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
UPDATE:
Как подсказал @lau, я попытался переместить AVG в CTE, но получил другую синтаксическую ошибку:
WITH last_week_moves AS (
SELECT
m.gid,
m.uid,
TO_CHAR(AVG(m.played - LAG(m.played) OVER(PARTITION BY m.gid ORDER BY played)), 'HH24:MI') AS diff
FROM words_moves m
JOIN words_games g ON (m.gid = g.gid AND 5 IN (g.player1, g.player2))
WHERE m.played > CURRENT_TIMESTAMP - INTERVAL '1 week'
GROUP BY uid
)
UPDATE words_users SET
avg_score = (SELECT ROUND(AVG(score), 1) FROM words_moves WHERE uid = 5),
avg_time = diff
FROM last_week_moves
WHERE uid = 5;
ERROR: 42803: aggregate function calls cannot contain window function calls
LINE 5: TO_CHAR(AVG(m.played - LAG(m.played)...
^
LOCATION: check_agg_arguments_walker, parse_agg.c:728