我有API从DB返回圆顶分页行。它工作正常,但是当我按RANDOM()
订购行时,我会在连续的页面上得到重复。是否有为每个查询设置随机种子的选项?
如果不是,是否可以全局设置随机SEED以强制RANDOM()
为每个查询生成相同的值?然后我可以每隔3分钟改变一次全局随机或类似的东西......
你使用这段代码:
SELECT * FROM "table" ORDER BY RANDOM() OFFSET 5 LIMIT 5
现在我希望将种子传递给此查询,以便我可以对随机结果进行分页。 我应该这样做吗?:
SELECT "table".*, SETSEED(0.1) FROM "table" ORDER BY RANDOM() OFFSET 5 LIMIT 5
SELECT "table".*, SETSEED(0.1) FROM "table" ORDER BY RANDOM() OFFSET 10 LIMIT 5
结果将正确分页?
答案 0 :(得分:6)
你说"随机" order,这是你在调用ORDER BY random()
时获得的 - 对于每一行,PostgreSQL调用random()
,获取一个值,并使用它来决定如何在结果集中对该行进行排序。
为了使这个可重复,你必须弄乱种子。这感觉很蠢。根据{{3}}:
效果将持续到会话结束,除非被另一个SET
覆盖
我认为这意味着在使用连接池时,setseed
会改变使用该连接的下一个进程的连接。
我有一个案例,我不需要真正的随机性。我的标准是:
例如,这没关系:
为了获得这样的东西,模数似乎运作良好。例如,ORDER BY id % 7, id
表示请求1的所有页面,ORDER BY id % 11, id
表示请求2的所有页面。也就是说,对于每一行,将其id除以模数并按余数排序。在具有相同余数的行中,按id排序(以确保排序稳定)。
可以为第一页随机选取模数,然后将其重新用作每个后续页面请求的参数。
您可以看到这对您的数据库有何影响:
echo "select id, id % 7 FROM my_table ORDER BY id % 77, id" | psql my_db > sort.txt
素数模数可能会给你最大的变化。如果你的id从1开始(这样% 77
会使前77行以正常顺序返回),你可以尝试在时间戳字段上执行模数。例如:
ORDER BY (extract(epoch from inserted_at)* 100000)::bigint % 77
但是你需要一个功能索引来提高效率。
答案 1 :(得分:4)
使用此union all
技术,随机顺序是可重复的
select a, b
from (
select setseed(0.1), null as a, null as b
union all
select null, a, b
from t
offset 1
) s
order by random()
offset 0
limit 5
;
答案 2 :(得分:2)
您可以使用setseed(dp)
种子random()
种子[-1.0,1.0]。 E.g:
engine=> SELECT SETSEED(0.16111981);
setseed
---------
(1 row)
engine=> SELECT RANDOM();
random
-------------------
0.205839179921895
(1 row)
engine=> SELECT RANDOM();
random
-------------------
0.379503262229264
(1 row)
engine=> SELECT RANDOM();
random
-------------------
0.268553872592747
(1 row)
engine=> SELECT RANDOM();
random
-------------------
0.788029655814171
(1 row)
当然,每次重新种植时,您都会得到完全相同的结果:
engine=> SELECT SETSEED(0.16111981), RANDOM();
setseed | random
---------+-------------------
| 0.205839179921895
(1 row)
engine=> SELECT SETSEED(0.16111981), RANDOM();
setseed | random
---------+-------------------
| 0.205839179921895
(1 row)
engine=> SELECT SETSEED(0.16111981), RANDOM();
setseed | random
---------+-------------------
| 0.205839179921895
(1 row)
engine=> SELECT SETSEED(0.16111981), RANDOM();
setseed | random
---------+-------------------
| 0.205839179921895
(澄清:输出是从psql
复制的,引擎是我数据库的名称)
答案 3 :(得分:0)
此查询将再次以正确的顺序为您提供ID为4、2、1和4的行。
SELECT items.id, items.name
FROM items
-- unnest expands array values into rows
INNER JOIN unnest(ARRAY[4,2,1,4]) AS item_id
ON items.id = item_id
收益
id | name
----+---------------
4 | Toast Mitten
2 | Pickle Juicer
1 | Horse Paint
4 | Toast Mitten
知道这一点,您可以根据需要提供每个页面上应包含的ID。
例如,您可以SELECT id FROM items ORDER BY random()
,将列表分成(例如)5个ID的“页面”,并将其保存在应用程序内存,Redis中或任何地方。对于请求的每个页面,您将使用正确的ID页面运行上面的查询。
变化:
pgcrypto
和ORDER BY gen_random_uuid()
。ORDER BY
并以您的编程语言将id随机排列在内存中。答案 4 :(得分:0)
对于主键为UUID(或其他某种哈希)的表,一个非常快速且肮脏的选项是按UUID的子字符串对结果进行排序。在您的应用中,随机生成1到36之间的一些正整数,并在查询中使用这些位置。
例如,如果我要五个数字并生成{23,12,35,16,3},我将使用:
select * from (
select
organization_id,
substring(cast(organization_id as text) from 23 for 1) as o1,
substring(cast(organization_id as text) from 12 for 1) as o2,
substring(cast(organization_id as text) from 35 for 1) as o3,
substring(cast(organization_id as text) from 16 for 1) as o4,
substring(cast(organization_id as text) from 3 for 1) as o5
from channels_organizations
) t order by o1, o2, o3, o4, o5;
这将生成如下结果,您可以轻松分页:
organization_id o1 o2 o3 o4 o5
a059cd76-9d91-48db-8982-986fcd217b2a 2 9 2 8 5
3ce14f26-3e56-46eb-9a74-22862cc3ed4e 4 5 4 6 e
8e115b7e-2e7e-480e-9bc6-296deff3ed87 6 7 8 8 1
e1969c52-5028-47da-92ea-9f2918dcbf4d a 2 4 7 9
42eb7292-e881-4a04-b83a-3bf78548dab4 a 8 b a e
e8a33112-532f-4fec-b25b-416c5409ac7e b 2 7 f a
a763efaa-79a4-4cfa-92bc-803ebc5ff221 c a 2 c 6
581cae5b-5000-4aa6-837d-002ccf806e28 d 0 2 a 1
6b0ed7b4-b44d-4a51-910f-f3f2f0354d55 f 4 5 a 0
9369a547-f7e0-43e7-96ef-62bf631a0f0b f e 0 3 6
在此示例中,我在UUID格式的每个组中选择一个索引,并在将其发送到数据库之前对它们进行随机排序。
如果您不关心伪随机性,可以通过只使用一个子字符串索引来简化此操作,尽管您希望将子字符串长度(n
中的from x for n
)增加到至少3,因为您有可能在子字符串中以破折号结尾。