代码之家  ›  专栏  ›  技术社区  ›  Alexander Farber

使用带更新的CTE和聚合函数

  •  0
  • Alexander Farber  · 技术社区  · 6 年前

    在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在一个游戏中为两个玩家提供两次移动之间的时间:

    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 查询?

    下面是3个有问题的表:

     # \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
    

    更新:

    正如@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
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Gordon Linoff    6 年前

    你似乎想要:

    WITH last_week_moves AS (
          SELECT m.uid,
                 (MAX(m.played) - MIN(m.played)) / NULLIF(COUNT(*) - 1, 0) as avg_diff,
                 AVG(score) as avg_score
          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 m.uid
         )
    UPDATE words_users wu
        SET avg_score = lwm.avg_score,
            avg_time = TO_CHAR(avg_diff, 'HH24:MI')
    FROM last_week_moves lwm
    WHERE wu.uid = lwm.uid AND
          wu.uid = 5;
    

    注意,这简化了 diff 计算,所以 lag() 不需要。

    您可以看到,它们是等效的: 价值差异 一 4 3 9 5

    差异的平均值明显是4。这是 ((4 - 1) + (9 - 4)) / 2 . 你看到“4”是取消的,所以它是真的 (9 - 1) / 2 . 这种观察概括了。