我有两个表,custassets
和tags
。为了生成一些测试数据,我想要INSERT INTO
一个带有SELECT
的多对多表,从每个表中获取随机行(这样一个表中的随机主键与之配对)来自第二个的随机主键)。令我惊讶的是,这并不像我最初想的那么容易,所以我坚持用这个来自学。
这是我的第一次尝试。我选择10 custassets
和3 tags
,但两者在每种情况下都相同。第一个表被修复后我会没事的,但是我想随机分配分配的标签。
SELECT
custassets_rand.id custassets_id,
tags_rand.id tags_rand_id
FROM
(
SELECT id FROM custassets WHERE defunct = false ORDER BY RANDOM() LIMIT 10
) AS custassets_rand
,
(
SELECT id FROM tags WHERE defunct = false ORDER BY RANDOM() LIMIT 3
) AS tags_rand
这会产生:
custassets_id | tags_rand_id
---------------+--------------
9849 | 3322 }
9849 | 4871 } this pattern of tag PKs is repeated
9849 | 5188 }
12145 | 3322
12145 | 4871
12145 | 5188
17837 | 3322
17837 | 4871
17837 | 5188
....
然后我尝试了以下方法:在RANDOM()
列列表中进行第二次SELECT
调用。然而,这个更糟糕,因为它选择单个标签PK并坚持使用它。
SELECT
custassets_rand.id custassets_id,
(SELECT id FROM tags WHERE defunct = false ORDER BY RANDOM() LIMIT 1) tags_rand_id
FROM
(
SELECT id FROM custassets WHERE defunct = false ORDER BY RANDOM() LIMIT 30
) AS custassets_rand
结果:
custassets_id | tags_rand_id
---------------+--------------
16694 | 1537
14204 | 1537
23823 | 1537
34799 | 1537
36388 | 1537
....
这在脚本语言中很容易,我相信使用存储过程或临时表可以很容易地完成。但是我可以使用INSERT INTO SELECT
吗?
我确实考虑过使用随机函数选择整数主键,但遗憾的是两个表的主键在增量序列中都有间隙(因此每个表中可能会选择一个空行)。那本来没问题!
答案 0 :(得分:12)
更新以使用通常更快的子查询替换CTE。
要生成真正随机的组合,只需将rn
随机化为更大的集合:
SELECT c_id, t_id
FROM (
SELECT id AS c_id, row_number() OVER (ORDER BY random()) AS rn
FROM custassets
) x
JOIN (SELECT id AS t_id, row_number() OVER () AS rn FROM tags) y USING (rn);
如果任意组合足够好,这会更快(特别是对于大表):
SELECT c_id, t_id
FROM (SELECT id AS c_id, row_number() OVER () AS rn FROM custassets) x
JOIN (SELECT id AS t_id, row_number() OVER () AS rn FROM tags) y USING (rn);
如果两个表中的行数不匹配,并且您不想丢失较大表中的行,请使用modulo operator %
多次连接较小表中的行:
SELECT c_id, t_id
FROM (
SELECT id AS c_id, row_number() OVER () AS rn
FROM custassets -- table with fewer rows
) x
JOIN (
SELECT id AS t_id, (row_number() OVER () % small.ct) + 1 AS rn
FROM tags
, (SELECT count(*) AS ct FROM custassets) AS small
) y USING (rn);
正如我的评论所述,window functions (with appended OVER
clause)在PostgreSQL 8.4或更高版本中可用。
答案 1 :(得分:4)
WITH a_ttl AS (
SELECT count(*) AS ttl FROM custassets c),
b_ttl AS (
SELECT count(*) AS ttl FROM tags),
rows AS (
SELECT gs.*
FROM generate_series(1,
(SELECT max(ttl) AS ttl FROM
(SELECT ttl FROM a_ttl UNION SELECT ttl FROM b_ttl) AS m))
AS gs(row)),
tab_a_rand AS (
SELECT custassets_id, row_number() OVER (order by random()) as row
FROM custassets),
tab_b_rand AS (
SELECT id, row_number() OVER (order by random()) as row
FROM tags)
SELECT a.custassets_id, b.id
FROM rows r
JOIN a_ttl ON 1=1 JOIN b_ttl ON 1=1
LEFT JOIN tab_a_rand a ON a.row = (r.row % a_ttl.ttl)+1
LEFT JOIN tab_b_rand b ON b.row = (r.row % b_ttl.ttl)+1
ORDER BY 1,2;
您可以在SQL Fiddle上测试此查询。
答案 2 :(得分:2)
这是一种从随机选择2个表中的单个组合的不同方法,假设有两个表a
和b
,两个表都有主键id
。这些表的大小不必相同,第二行是从第一行中独立选择的,这对于testdata来说可能并不重要。
SELECT * FROM a, b
WHERE a.id = (
SELECT id
FROM a
OFFSET (
SELECT random () * (SELECT count(*) FROM a)
)
LIMIT 1)
AND b.id = (
SELECT id
FROM b
OFFSET (
SELECT random () * (SELECT count(*) FROM b)
)
LIMIT 1);
使用两个表进行测试,其中一个表为7000行,一行为100k行,结果为:immediate。对于多个结果,您必须重复调用查询 - 增加LIMIT并将x.id =
更改为x.id IN
将产生(aA,aB,bA,bB)结果模式。
答案 3 :(得分:1)
让我感到困惑的是,经过这么多年的关系数据库之后,似乎没有非常好的跨数据库方式来做这样的事情。 MSDN文章http://msdn.microsoft.com/en-us/library/cc441928.aspx似乎有一些有趣的想法,但当然不是PostgreSQL。即便如此,他们的解决方案需要一次通过,而我认为它应该可以在没有扫描的情况下完成。
我可以想象一些没有传递(在选择中)可能有效的方法,但它会涉及创建另一个表,将表的主键映射到随机数(或者随后随机选择的线性序列,在某些方式可能实际上更好),当然,这也可能有问题。
我意识到这可能是一个无用的评论,我只是觉得我需要咆哮。
答案 4 :(得分:1)
如果您只想从每一侧获取一组随机行,请使用伪随机数生成器。我会使用类似的东西:
select *
from (select a.*, row_number() over (order by NULL) as rownum -- NULL may not work, "(SELECT NULL)" works in MSSQL
from a
) a cross join
(select b.*, row_number() over (order by NULL) as rownum
from b
) b
where a.rownum <= 30 and b.rownum <= 30
这是一个笛卡尔积,它返回900行,假设a和b各有至少30行。
但是,我将您的问题解释为随机组合。再一次,我会选择伪随机方法。
select *
from (select a.*, row_number() over (order by NULL) as rownum -- NULL may not work, "(SELECT NULL)" works in MSSQL
from a
) a cross join
(select b.*, row_number() over (order by NULL) as rownum
from b
) b
where modf(a.rownum*107+b.rownum*257+17, 101) < <some vaue>
这让你得到任意行之间的组合。
答案 5 :(得分:1)
只是一个简单的笛卡尔积随机()似乎运作得相当好。简单的纪念活动...
-- Cartesian product
-- EXPLAIN ANALYZE
INSERT INTO dirgraph(point_from,point_to,costs)
SELECT p1.the_point , p2.the_point, (1000*random() ) +1
FROM allpoints p1
JOIN allpoints p2 ON random() < 0.002
;