2つのテーブルndefined row countに名前と値を指定した場合、関数のピボットされた_CROSS JOIN
_をそれらの値の上に表示するにはどうすればよいですか。
_CREATE TEMP TABLE foo AS
SELECT x::text AS name, x::int
FROM generate_series(1,10) AS t(x);
CREATE TEMP TABLE bar AS
SELECT x::text AS name, x::int
FROM generate_series(1,5) AS t(x);
_
たとえば、その関数が乗算である場合、以下のような(乗算)テーブルをどのように生成しますか?
これらすべての_(arg1,arg2,result)
_行は、
_SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x AS result
FROM foo
CROSS JOIN bar;
_
したがって、これはプレゼンテーションの問題にすぎませんこれもカスタム名で機能させたい-テキストへの単なる引数CAST
edではなく、テーブルに設定された名前、
_CREATE TEMP TABLE foo AS
SELECT chr(x+64) AS name, x::int
FROM generate_series(1,10) AS t(x);
CREATE TEMP TABLE bar AS
SELECT chr(x+72) AS name, x::int
FROM generate_series(1,5) AS t(x);
_
これは、ダイナミックリターンタイプが可能なCROSSTABで簡単に実行できると思います。
_SELECT * FROM crosstab(
'
SELECT foo.x AS arg1, bar.x AS arg2, foo.x*bar.x
FROM foo
CROSS JOIN bar
', 'SELECT DISTINCT name FROM bar'
) AS **MAGIC**
_
しかし、_**MAGIC**
_がないと、
_ERROR: a column definition list is required for functions returning "record" LINE 1: SELECT * FROM crosstab(
_
参考までに、上記の例と名前を使用すると、これはtablefunc
のcrosstab()
が必要とするものに似ています。
_SELECT * FROM crosstab(
'
SELECT foo.x AS arg1, bar.x AS arg2, foo.x*bar.x
FROM foo
CROSS JOIN bar
'
) AS t(row int, i int, j int, k int, l int, m int);
_
しかし、ここで、例のbar
テーブルの内容とサイズについての仮定に戻ります。だから、
そのようなプレゼンテーションを生成するために「列定義リスト」なしでPostgreSQLでできることは何ですか?
単純な場合のcrosstab()
を使用したnon-dynamicソリューション:
_SELECT * FROM crosstab(
'SELECT b.x, f.name, f.x * b.x AS prod
FROM foo f, bar b
ORDER BY 1, 2'
) AS ct (x int, "A" int, "B" int, "C" int, "D" int, "E" int
, "F" int, "G" int, "H" int, "I" int, "J" int);
_
結果の列を_foo.name
_ではなく_foo.x
_で並べ替えます。両方が並行してソートされることはありますが、それは単純な設定です。ケースに適した並べ替え順を選択してください。 2番目の列の実際の値は、このクエリでは関係ありません(crosstab()
の1パラメータ形式)。
定義では欠損値がないため、2つのパラメーターを指定したcrosstab()
も必要ありません。見る:
(後の編集でfoo
をbar
に置き換えることにより、質問のクロス集計クエリを修正しました。これによりクエリも修正されますが、foo
の名前を引き続き使用できます。)
列の名前と型を動的にすることはできません。 SQLは、呼び出し時に結果の列の数、名前、およびタイプを知る必要があります。明示的な宣言、またはシステムカタログの情報から(これが_SELECT * FROM tbl
_で発生することです。Postgresは登録されたテーブル定義を検索します。)
Postgresがユーザーテーブルのdataから結果の列を導出するようにします。 起こりません
いずれにしても、サーバーへのtwoラウンドトリップが必要です。カーソルを作成してから、それをウォークスルーします。または、一時テーブルを作成してからそれを選択します。または、タイプを登録して、呼び出しで使用します。
または、1つのステップでクエリを生成し、次のステップで実行するだけです。
_SELECT $$SELECT * FROM crosstab(
'SELECT b.x, f.name, f.x * b.x AS prod
FROM foo f, bar b
ORDER BY 1, 2'
) AS ct (x int, $$
|| string_agg(quote_ident(name), ' int, ' ORDER BY name) || ' int)'
FROM foo;
_
これにより、上記のクエリが動的に生成されます。次のステップで実行します。
ネストされた引用符の処理を簡単にするために、ドル引用符(_$$
_)を使用しています。見る:
quote_ident()
は、違法な列名をエスケープするために不可欠です(そしてSQLインジェクションから守るためかもしれません)。
関連:
そのようなプレゼンテーションを生成するために「列定義リスト」なしでPostgreSQLでできることは何ですか?
これをプレゼンテーションの問題としてフレーム化する場合は、クエリ後のプレゼンテーション機能を検討することができます。
psql
(9.6)の新しいバージョンには\crosstabview
が付属し、SQLサポートなしでクロスタブ表現で結果を表示します(@Erwinの回答で述べたように、SQLはこれを直接生成できないため、SQLは、呼び出し時に結果の列の数、名前、およびタイプを知ることを要求します)
たとえば、最初のクエリでは次のようになります。
SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x AS result
FROM foo
CROSS JOIN bar
\crosstabview
arg1 | 1 | 2 | 3 | 4 | 5
------+----+----+----+----+----
1 | 1 | 2 | 3 | 4 | 5
2 | 2 | 4 | 6 | 8 | 10
3 | 3 | 6 | 9 | 12 | 15
4 | 4 | 8 | 12 | 16 | 20
5 | 5 | 10 | 15 | 20 | 25
6 | 6 | 12 | 18 | 24 | 30
7 | 7 | 14 | 21 | 28 | 35
8 | 8 | 16 | 24 | 32 | 40
9 | 9 | 18 | 27 | 36 | 45
10 | 10 | 20 | 30 | 40 | 50
(10 rows)
ASCII列名を使用した2番目の例では、次のようになります。
SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x
FROM foo
CROSS JOIN bar
\crosstabview
arg1 | I | J | K | L | M
------+----+----+----+----+----
A | 1 | 2 | 3 | 4 | 5
B | 2 | 4 | 6 | 8 | 10
C | 3 | 6 | 9 | 12 | 15
D | 4 | 8 | 12 | 16 | 20
E | 5 | 10 | 15 | 20 | 25
F | 6 | 12 | 18 | 24 | 30
G | 7 | 14 | 21 | 28 | 35
H | 8 | 16 | 24 | 32 | 40
I | 9 | 18 | 27 | 36 | 45
J | 10 | 20 | 30 | 40 | 50
(10 rows)
詳細は psql manual および https://wiki.postgresql.org/wiki/Crosstabview を参照してください。
これは決定的な解決策ではありません
これが今までの私の最善のアプローチです。最終的な配列を列に変換する必要があります。
まず、両方のテーブルのデカルト積を取得します。
select foo.name xname, bar.name yname, (foo.x * bar.x)::text as val,
((row_number() over ()) - 1) / (select count(*)::integer from foo) as row
from bar
cross join foo
order by bar.name, foo.name
ただし、最初のテーブルのすべての行を識別するためだけに行番号を追加しました。
((row_number() over ()) - 1) / (select count(*)::integer from foo)
次に、この形式で結果を作成しました:
[Row name] [Array of values]
select col_name, values
from
(
select '' as col_name, array_agg(name) as values from foo
UNION
select fy.name as col_name,
(select array_agg(t.val) as values
from
(select foo.name xname, bar.name yname, (foo.x * bar.x)::text as val,
((row_number() over ()) - 1) / (select count(*)::integer from foo) as row
from bar
cross join foo
order by bar.name, foo.name) t
where t.row = fy.row)
from
(select name, (row_number() over(order by name)) - 1 as row from bar) fy
) a
order by col_name;
+---+---------------------+
| | ABCDEFGHIJ |
+---+---------------------+
| I | 12345678910 |
+---+---------------------+
| J | 2468101214161820 |
+---+---------------------+
| K | 36912151821242730 |
+---+---------------------+
| L | 481216202428323640 |
+---+---------------------+
| M | 5101520253035404550 |
+---+---------------------+
カンマ区切りの文字列に変換します。
select col_name, values
from
(
select '' as col_name, array_to_string(array_agg(name),',') as values from foo
UNION
select fy.name as col_name,
(select array_to_string(array_agg(t.val),',') as values
from
(select foo.name xname, bar.name yname, (foo.x * bar.x)::text as val,
((row_number() over ()) - 1) / (select count(*)::integer from foo) as row
from bar
cross join foo
order by bar.name, foo.name) t
where t.row = fy.row)
from
(select name, (row_number() over(order by name)) - 1 as row from bar) fy
) a
order by col_name;
+---+------------------------------+
| | A,B,C,D,E,F,G,H,I,J |
+---+------------------------------+
| I | 1,2,3,4,5,6,7,8,9,10 |
+---+------------------------------+
| J | 2,4,6,8,10,12,14,16,18,20 |
+---+------------------------------+
| K | 3,6,9,12,15,18,21,24,27,30 |
+---+------------------------------+
| L | 4,8,12,16,20,24,28,32,36,40 |
+---+------------------------------+
| M | 5,10,15,20,25,30,35,40,45,50 |
+---+------------------------------+
(後で試すだけです: http://rextester.com/NBCYXA218 )
補足として、SQL:2016は ポリモーフィックテーブル関数(ISO/IEC TR 19075-7:2017) でこれに対応するようです
私はリンク What's New in SQL:2016 を見つけましたが、著者はこれについて詳しく説明していません。