Sql 将限制添加到数组\u到\u JSON或数组\u AGG
在使用PostgreSQL 9.6.6作为后端的两人游戏中,我定义了以下自定义存储函数,用于检索用户的聊天信息:Sql 将限制添加到数组\u到\u JSON或数组\u AGG,sql,postgresql,postgresql-9.6,postgresql-json,array-agg,Sql,Postgresql,Postgresql 9.6,Postgresql Json,Array Agg,在使用PostgreSQL 9.6.6作为后端的两人游戏中,我定义了以下自定义存储函数,用于检索用户的聊天信息: CREATE OR REPLACE FUNCTION words_get_user_chat( in_uid integer ) RETURNS jsonb AS $func$ SELECT COALESCE( JSONB_OBJECT_AGG(gid, ARRAY_TO_JSON(y))
CREATE OR REPLACE FUNCTION words_get_user_chat(
in_uid integer
) RETURNS jsonb AS
$func$
SELECT COALESCE(
JSONB_OBJECT_AGG(gid, ARRAY_TO_JSON(y)),
'{}'::jsonb
) FROM (
SELECT c.gid,
ARRAY_AGG(
JSON_BUILD_OBJECT(
'created', EXTRACT(EPOCH FROM c.created)::int,
'uid', c.uid,
'msg', c.msg
)
ORDER BY c.created ASC
) AS y
FROM words_chat c
LEFT JOIN words_games g
USING (gid)
WHERE in_uid in (g.player1, g.player2)
AND (g.finished IS NULL OR g.finished > CURRENT_TIMESTAMP - INTERVAL '1 day')
GROUP BY c.gid
/* LIMIT 10 */
) AS x;
$func$ LANGUAGE sql STABLE;
它连接words_游戏和words_聊天表,并生成以下JSON对象,其游戏id为9,作为一个字符串键,包含一个包含消息的JSON数组:
# select words_get_user_chat(6);
words_get_user_chat
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
{"9": [{"msg": "test2", "uid": 6, "created": 1516445342}, {"msg": "test3", "uid": 6, "created": 1516445358}, {"msg": "test4", "uid": 6, "created": 1516445369
}, {"msg": "test5", "uid": 6, "created": 1516445381}, {"msg": "test6", "uid": 6, "created": 1516445405}, {"msg": "test7", "uid": 6, "created": 1516445415}, {"
msg": "test8", "uid": 6, "created": 1516445508}, {"msg": "test9", "uid": 6, "created": 1516445539}, {"msg": "test10", "uid": 6, "created": 1516445743}, {"msg"
: "test11", "uid": 6, "created": 1516445989}, {"msg": "test12", "uid": 6, "created": 1516446101}, {"msg": "test13", "uid": 6, "created": 1516446125}, {"msg":
"test14", "uid": 6, "created": 1516446145}, {"msg": "test15", "uid": 6, "created": 1516446227}, {"msg": "test16", "uid": 6, "created": 1516446652}, {"msg": "t
est17", "uid": 6, "created": 1516446999}, {"msg": "test18", "uid": 6, "created": 1516447168}, {"msg": "test19", "uid": 6, "created": 1516447229}, {"msg": "tes
t20", "uid": 6, "created": 1516447493}, {"msg": "test21", "uid": 6, "created": 1516447532}, {"msg": "test22", "uid": 6, "created": 1516447555}, {"msg": "test2
3", "uid": 6, "created": 1516448017}, {"msg": "test24", "uid": 6, "created": 1516448062}]}
(1 row)
这很有效,但我想对数组元素的数量增加10个限制,作为一种防止聊天泛滥的措施
我曾尝试将其添加到函数中,请参见上面的注释行,但没有效果
你能建议10号限额的正确地点吗
我通过WebSockets将JSON对象发送到Android应用程序,并希望防止恶意用户通过泛滥聊天来破坏这些对象的大小
更新:
我正在尝试迈克的建议:
CREATE OR REPLACE FUNCTION words_get_user_chat(
in_uid integer
) RETURNS jsonb AS
$func$
SELECT COALESCE(
JSONB_OBJECT_AGG(gid, ARRAY_TO_JSON(y)),
'{}'::jsonb
) FROM (
SELECT c.gid,
ROW_NUMBER() OVER (PARTITION BY c.gid) AS rn,
ARRAY_AGG(
JSON_BUILD_OBJECT(
'created', EXTRACT(EPOCH FROM c.created)::int,
'uid', c.uid,
'msg', c.msg
)
ORDER BY c.created ASC
) AS y
FROM words_chat c
LEFT JOIN words_games g
USING (gid)
WHERE in_uid in (g.player1, g.player2)
AND (g.finished IS NULL OR g.finished > CURRENT_TIMESTAMP - INTERVAL '1 day')
AND rn < 10
GROUP BY c.gid
) AS x;
$func$ LANGUAGE sql STABLE;
但不幸的是,出现了语法错误:
ERROR: 42703: column "rn" does not exist
LINE 24: AND rn < 10
^
LOCATION: errorMissingColumn, parse_relation.c:3194
更新2:
这是我使用的两个表,很抱歉之前没有包括这些信息-
#\d words_chat
Table "public.words_chat"
Column | Type | Modifiers
---------+--------------------------+----------------------------------------------------------
cid | bigint | not null default nextval('words_chat_cid_seq'::regclass)
created | timestamp with time zone | not null
gid | integer | not null
uid | integer | not null
msg | text | not null
Indexes:
"words_chat_pkey" PRIMARY KEY, btree (cid)
Foreign-key constraints:
"words_chat_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
"words_chat_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
# \d words_games
Table "public.words_games"
Column | Type | Modifiers
----------+--------------------------+-----------------------------------------------------------
gid | integer | not null default 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 |
hint1 | text |
hint2 | text |
score1 | integer | not null
score2 | 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
Indexes:
"words_games_pkey" PRIMARY KEY, btree (gid)
Check constraints:
"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
也许能回答你的问题。试着做:
(ARRAY_AGG( /* same as before */ ))[1:10] AS y
您的函数应如下所示:
CREATE OR REPLACE FUNCTION words_get_user_chat(in_uid integer)
RETURNS jsonb AS
$func$
SELECT COALESCE(jsonb_object_agg(gid, y), '{}')
FROM (
SELECT gid, jsonb_agg((SELECT j FROM (SELECT created, uid, msg) j)) AS y
FROM (
SELECT DISTINCT gid -- DISTINCT may be redundant
FROM words_games
WHERE (finished IS NULL
OR finished > (CURRENT_TIMESTAMP - INTERVAL '1 day'))
AND in_uid IN (player1, player2)
) g
CROSS JOIN LATERAL (
SELECT EXTRACT(EPOCH FROM created)::int AS created
, uid
, msg
FROM words_chat c
WHERE c.gid = g.gid
ORDER BY c.created DESC
LIMIT 10 -- HERE !!
) c
GROUP BY 1
) x
$func$ LANGUAGE sql STABLE;
不要聚合所有行,只是为了以后丢弃多余的行。那将是一种浪费。在子查询中将限额放在ORDER BY之后
为此,您需要首先从words_游戏中识别符合条件的gid,然后使用横向连接到words_chat上的子查询。也应该是正确的和更快的
由于c.created定义为NOTNULL,因此不需要在ORDERBY子句中最后添加NULL。此匹配的多列索引应产生最佳读取性能:
CREATE INDEX ON words_chat(gid, created DESC);
也许还有一些文字游戏的索引。取决于基数和值频率
在这样做的同时,我还简化了jsonb结果的构造
相关的:
使用选择。。。从select*,行数由c.gid作为RN从…..超额分配。。。。。X感谢Mike,但不幸的是,我不知道如何应用您的建议-请查看更新,以便合适吗?并且需要添加ORDER BY:ROW_NUMBER OVER PARTITION BY c.gid ORDER BY xxxx以获取最新或其他需要的recordsWorks,但比必要的要昂贵得多。不幸的是,它返回最旧的聊天消息1到10,而我需要最新的10,然后将ORDER BY更改为DESCHmm,然后我必须在我的Android应用程序中对其进行排序,这是我不想做的。。。我更喜欢从PostgreSQL数组的末尾开始,但还没有找到方法:测试选择数组[1,2,3,4,5,6][3];不工作,因为我会期待它…这是完美的工作,谢谢!您认为我应该为创建的列添加索引吗,因为我使用ORDER BY它?我用两个表的描述更新了我的问题。