1

VS 2008 で C# を使用して、2 つの入力パラメーターと 2 つの出力パラメーターを持つ PostgreSQL ストアド プロシージャからデータを取得しています。プロシージャを作成したとき、PostgreSQL から、レコードを返すように指定する必要があると言われました。

VS2008 では、この手順を使用する最初の試みとして、OdbcCommand型のオブジェクトを作成し、CommandType.StoredProcedureそれに 4 つのパラメーター (入力の方向を持つ 2 つと出力の方向を持つ 2 つ) を与える必要がありました。ExecuteNonQuery()最初に を使用し、次に を使用して、コマンドはエラーなしで実行されましExecuteReader()たが、出力パラメータの値は null でした。リーダーのGetValues()関数を呼び出したところ、結果は string を含む単一のオブジェクトであることがわかりました"{3,4}"

次に、StackOverflow からの提案に従って、コマンド テキストを次のように変更しました: {call closest_idle_cover(?, ?, ?, ?)}

これも機能し、GetValues()int 型の 2 つのオブジェクト (1 つは 3、もう 1 つは 4) の配列が得られました。これは、文字列を解析する必要がないため、かなり改善されました。しかし、出力パラメーターにはまだ NULL 値が含まれており、実際、2 つの入力パラメーターのみを渡した場合でも、コマンドは同様に機能します。

それで、うまくいく解決策がありますが、私はまだ興味があります: 出力パラメーターに値を取得するにはどうすればよいですか?

PostgreSQL ストアド プロシージャは次のとおりです。

CREATE OR REPLACE FUNCTION plant_genie.closest_idle_cover(IN int, IN int, OUT int, OUT int)
  RETURNS record AS
$BODY$
DECLARE
    current_x ALIAS FOR $1;
    current_y ALIAS FOR $2;
    target_x ALIAS FOR $3;
    target_y ALIAS FOR $4;
    coverLocations ic_storage_locations%rowtype;
BEGIN
    target_x := 3;
    target_y := 4;  

    SELECT INTO coverLocations * 
    FROM ic_storage_locations 
    WHERE inner_cover IS NOT NULL 
    ORDER BY sqrt(pow(current_x - ic_storage_locations.x_coordinate, 2) + 
            pow(current_y - ic_storage_locations.y_coordinate, 2))
    LIMIT 1;

    IF FOUND THEN
        INSERT INTO op_messages (message) VALUES ('Found a cover location record.');
        target_x := coverLocations.x_coordinate;
        target_y := coverLocations.y_coordinate;
    ELSE
        INSERT INTO op_messages (message) VALUES ('Could not find a cover location record.');
    END IF;
END;
$BODY$ LANGUAGE 'plpgsql' VOLATILE COST 100;
4

1 に答える 1

1

関数の本体に明示的なステートメントを持たずに、OUTパラメーターだけでなく句も使用しています。その組み合わせは機能しません。パラメーターを使用するよりも洗練された解決策は、出力テーブルの形式を定義することです。何が起こっているかはより明白です。RETURNS recordRETURNOUT

CREATE OR REPLACE FUNCTION plant_genie.closest_idle_cover(current_x int, current_y int)
RETURNS TABLE (target_x int, target_y int) AS $BODY$
DECLARE
    coverLocations ic_storage_locations%rowtype;
BEGIN
    SELECT INTO coverLocations * 
    FROM ic_storage_locations 
    WHERE inner_cover IS NOT NULL 
    ORDER BY pow(current_x - ic_storage_locations.x_coordinate, 2) + 
             pow(current_y - ic_storage_locations.y_coordinate, 2)
    LIMIT 1;

    IF FOUND THEN
        INSERT INTO op_messages (message) VALUES ('Found a cover location record.');
        RETURN QUERY SELECT coverLocations.x_coordinate, coverLocations.y_coordinate;
    ELSE
        INSERT INTO op_messages (message) VALUES ('Could not find a cover location record.');
    END IF;
END; $BODY$ LANGUAGE 'plpgsql' STRICT;

したがって、この関数を呼び出すと、少なくとも 1 つの ic_storage_location が null でない場合にレコードが返されます。

SELECT * FROM plant_genie.closest_idle_cover(1, 2);

データベースから引き出す他のデータと同じように、C# コードでこれを処理できます。

いくつかの観察:

  • 最も近い ic_storage_location を探しているので、SQRT()計算コストの高い関数呼び出しを省くことができます。二乗和を操作するだけで、現在の場所からの距離でレコードを並べ替えるという同じプロパティがあります。
  • 関数が定義されているのSTRICTは、両方のパラメーターの値が正しく機能する必要があるためです。
  • 自分が何をしているのか本当にCOSTわかっていない限り、自分で値を割り当てないでください。
于 2014-05-11T04:20:03.993 に答える