0

デフォルトの順序のみに基づいて 2 セットのレコードを結合するにはどうすればよいですか?

したがって、テーブル x(col(1,2,3,4,5,6,7)) と別のテーブル z(col(a,b,c,d,e,f,g)) がある場合

それは戻ってきます

c1 c2
-- --    
1   a
2   b
3   c
4   d
5   e
6   f
7   g

実際には、パラメーターから 1 次元配列のペアを結合し、それらをテーブルの列のように扱いたいと考えていました。

サンプルコード:

CREATE OR REPLACE FUNCTION "Test"(timestamp without time zone[],
                                  timestamp without time zone[])
  RETURNS refcursor AS
$BODY$
DECLARE
curr refcursor;
BEGIN
    OPEN curr FOR 
        SELECT DISTINCT "Start" AS x, "End" AS y, COUNT("A"."id") 
        FROM UNNEST($1) "Start" 
        INNER JOIN 
        (
            SELECT "End", ROW_NUMBER() OVER(ORDER BY ("End")) rn
                FROM UNNEST($2) "End" ORDER BY ("End") 
        ) "End" ON ROW_NUMBER() OVER(ORDER BY ("Start")) = "End".rn 
        LEFT JOIN "A" ON ("A"."date" BETWEEN x AND y) 
        GROUP BY 1,2 
        ORDER BY "Start";
    return curr;
END

$BODY$
4

3 に答える 3

3

さて、コメントで明らかにされた本当の質問に答えるために、それは次のように見えます:

2 つの配列 'a' と 'b' が与えられた場合、それらの要素をペアにして、要素のペアをクエリで列のエイリアスとして取得するにはどうすればよいですか?

これに対処するには、いくつかの方法があります。

  • 配列の長さが等しい場合にのみunnest、節で複数の関数を使用しますSELECT(下位互換性のためにのみ使用するべき非推奨の方法です)。

  • generate_subscripts配列をループするために使用します。

  • 古いバージョンをサポートする必要がある場合は、 generate_seriesover サブクエリを使用array_lowerarray_upperてエミュレートします。generate_subscriptsgenerate_subscripts

  • タプルを返す順序に依存し、unnest期待しています-私の他の答えのように、以下に示すように。動作しますが、将来のバージョンで動作することは保証されていません。

  • PostgreSQL 9.4 で追加された機能 (WITH ORDINALITY最初の投稿も参照) を使用して、9.4 がリリースされたときの行番号を取得unnestします。

  • UNNESTSQL 標準ですが、PostgreSQL ではまだサポートされていないmultiple-array を使用します。

したがって、arraypair配列パラメーターaとを持つ関数があるとしbます。

CREATE OR REPLACE FUNCTION arraypair (a integer[], b text[]) 
RETURNS TABLE (col_a integer, col_b text) AS $$
  -- blah code here blah
$$ LANGUAGE whatever IMMUTABLE;

そしてそれは次のように呼び出されます:

SELECT * FROM arraypair( ARRAY[1,2,3,4,5,6,7], ARRAY['a','b','c','d','e','f','g'] );

可能な関数定義は次のとおりです。

SRF-in- SELECT(非推奨)

CREATE OR REPLACE FUNCTION arraypair (a integer[], b text[])
RETURNS TABLE (col_a integer, col_b text) AS $$
    SELECT unnest(a), unnest(b);
$$ LANGUAGE sql IMMUTABLE;

配列の長さが等しくない場合、奇妙で予期しない結果が生成されます。SELECTその理由と正確に何が起こるかについては、セットを返す関数とリスト内の非標準的な使用に関するドキュメントを参照してください。

generate_subscripts

これはおそらく最も安全なオプションです。

CREATE OR REPLACE FUNCTION arraypair (a integer[], b text[])
RETURNS TABLE (col_a integer, col_b text) AS $$
    SELECT
       a[i], b[i]
    FROM generate_subscripts(CASE WHEN array_length(a,1) >= array_length(b,1) THEN a::text[] ELSE b::text[] END, 1) i;
$$ LANGUAGE sql IMMUTABLE;

配列の長さが等しくない場合、書かれているように、短い方には null 要素が返されるため、完全な外部結合のように機能します。ケースの意味を逆にすると、内部結合のような効果が得られます。この関数は、配列が 1 次元であり、インデックス 1 から始まると想定します。配列引数全体が NULL の場合、関数は NULL を返します。

より一般化されたバージョンは PL/PgSQL で記述され、 check array_ndims(a) = 1、 check array_lower(a, 1) = 1、null 配列のテストなどを行います。それはあなたに任せます。

ペアごとのリターンを期待:

これは動作することが保証されていませんが、PostgreSQL の現在のクエリ エグゼキュータでは動作します。

CREATE OR REPLACE FUNCTION arraypair (a integer[], b text[])
RETURNS TABLE (col_a integer, col_b text) AS $$
 WITH
    rn_c1(rn, col) AS (
      SELECT row_number() OVER (), c1.col
      FROM unnest(a) c1(col) 
    ),
    rn_c2(rn, col) AS (
      SELECT row_number() OVER (), c2.col
      FROM unnest(b) c2(col)
    )
    SELECT
      rn_c1.col AS c1, 
      rn_c2.col AS c2
    FROM rn_c1 
    INNER JOIN rn_c2 ON (rn_c1.rn = rn_c2.rn);
$$ LANGUAGE sql IMMUTABLE;

generate_subscriptsもっと安全に使用することを検討します。

複数引数unnest:

これは機能するはずですが、PostgreSQLunnestは (まだ) 複数の入力配列を受け入れないため機能しません。

SELECT * FROM unnest(a,b);
于 2013-07-15T04:04:22.167 に答える
0

「デフォルトの順序」とは、行が.select * from tablenameなしで返される順序を意味しているように思えますORDER BY

その場合、この順序はundefinedです。データベースは、任意の順序で行を返すことができます。UPDATE行の場合、おそらくテーブル内の別の位置に移動することがわかります。

テーブルに順序があると想定していたのに順序がないという状況に陥った場合は、回復オプションとして、テーブル内のタプルのディスク上の順序に基づいて行番号を追加できます。

select row_number() OVER (), *
from the_table
order by ctid

出力が正しいように見える場合はCREATE TABLE、追加のフィールドを含む新しいテーブルを作成し、 でINSERT INTO ... SELECT並べ替えられたデータを挿入してからテーブルを挿入し、最後に外部キー参照を修正して新しいテーブルを指すようにすることをお勧めします。ctidALTER TABLE ... RENAME

ctidUPDATEautovacuum 、 、CLUSTERなどで変更できるため、アプリケーションで使用する必要はありません。ここで使用しているのは、実際の順序付けキーまたは識別子キーがないように思われるためです。

ディスク上の順序に基づいて行をペアにする必要がある場合 (上記のように、信頼性が低く安全でないこと)、次のSQLFiddleを試すことができます。

WITH
rn_c1(rn, col) AS (
  SELECT row_number() OVER (ORDER BY ctid), c1.col
  FROM c1 
),
rn_c2(rn, col) AS (
  SELECT row_number() OVER (ORDER BY ctid), c2.col
  FROM c2
)
SELECT
  rn_c1.col AS c1, 
  rn_c2.col AS c2
FROM rn_c1 
INNER JOIN rn_c2 ON (rn_c1.rn = rn_c2.rn);

ただし、本番アプリでは決してこれに依存しないでください。本当に立ち往生している場合は、これを使用しCREATE TABLE ASて、必要なキーがない DB からデータを回復する作業を開始できる新しいテーブルを作成できますが、それだけです。

関数からの中間結果のように、ctid のないセットを使用する場合()の代わりに、上記と同じアプローチが空のウィンドウ句で機能する場合があります。(ORDER BY ctid)ただし、安全性はさらに低下するため、最後の手段としてのみ使用する必要があります。

(この新しい関連する回答も参照してください: https://stackoverflow.com/a/17762282/398670 )

于 2013-07-15T00:08:26.133 に答える