12

2つのテーブルがcustassetsありtagsます。INSERT INTOいくつかのテストデータを生成するために、それぞれからランダムな行を取得する多対多のテーブルを作成したいと思いSELECTます(1つのテーブルのランダムな主キーが2番目のテーブルのランダムな主キーとペアになるようにします)。驚いたことに、これは私が最初に思ったほど簡単ではないので、私は自分自身を教えるためにこれを続けています。

これが私の最初の試みです。10custassetsと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
....

次に、次のアプローチを試しました。列リストの2番目のRANDOM()呼び出しを実行します。SELECTただし、これは1つのタグ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ますか?

ランダム関数を使用して整数の主キーを選択することを考えましたが、残念ながら、両方のテーブルの主キーには増分シーケンスにギャップがあります(したがって、各テーブルで空の行が選択される可能性があります)。そうでなければそれは大丈夫だっただろう!

4

6 に答える 6

15

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);

両方のテーブルの行数が一致せず、大きい方のテーブルの行を失いたくない場合は、モジュロ演算子%を使用して小さい方のテーブルの行を複数回結合します。

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);

私のコメントで述べたように、ウィンドウ関数(OVER句が追加されている)はPostgreSQL8.4以降で使用できます。

于 2012-05-15T19:57:15.427 に答える
5
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;

このクエリはSQLFiddleでテストできます。

于 2012-05-15T20:02:31.820 に答える
2

aこれは、2つのテーブルとbが主キーであると仮定して、2つのテーブルから1つの組み合わせをランダムに選択する別のアプローチidです。テーブルは同じサイズである必要はなく、2番目の行は最初の行から独立して選択されます。これはテストデータにとってそれほど重要ではない場合があります。

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);

2つのテーブルでテストされました。1つはサイズ7000行、もう1つは100k行で、結果はすぐに得られます。複数の結果については、クエリを繰り返し呼び出す必要があります。LIMITを増やしてに変更x.id =するx.id INと、(aA、aB、bA、bB)の結果パターンが生成されます。

于 2012-05-25T15:33:15.497 に答える
1

何年にもわたるリレーショナルデータベースの後で、このようなことを行うための非常に優れたクロスデータベースの方法がないように思われることは私を悩ませます。MSDNの記事http://msdn.microsoft.com/en-us/library/cc441928.aspxには興味深いアイデアがあるようですが、もちろんそれはPostgreSQLではありません。それでも、スキャンなしで実行できるはずだと思うのに、ソリューションには1回のパスが必要です。

パスなしで(選択して)機能するいくつかの方法を想像できますが、テーブルの主キーを乱数(または後でランダムに選択する線形シーケンスにマップする別のテーブルを作成する必要があります。より良い)、そしてもちろん、それも問題があるかもしれません。

これはおそらく役に立たないコメントだと思います。少し怒鳴る必要があると感じました。

于 2012-05-15T15:22:47.413 に答える
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

これはデカルト積を実行しており、aとbがそれぞれ少なくとも30行あると仮定して900行を返します。

しかし、私はあなたの質問をランダムな組み合わせを得ると解釈しました。もう一度、疑似ランダムアプローチを使用します。

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>

これにより、任意の行の組み合わせを取得できます。

于 2012-05-15T16:20:02.873 に答える
1

単純な直積ONrandom()は、かなりうまく機能しているように見えます。シンプルなコムボンジュール...

-- 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
        ;
于 2012-05-16T12:05:07.597 に答える