web-dev-qa-db-ja.com

PostgreSQLのパラメーターとしてデータテーブルを渡す

Asp.net C#に1つのデータテーブルがあります。そして、そのデータテーブルをPostgreSQL関数にテーブルパラメータとして渡します。どのようにして可能ですか?

以下の例は同じですが、SQL Serverにあります。同じことが必要ですが、問題はSQL ServerではなくPostgreSQLをバックエンドとして使用していることです。

はじめに

SQL Serverのストアドプロシージャは、パラメーターとしてSystem.Data.DataTableをサポートしています。 System.Data.SqlParameterクラスを使用して提供したのと同じ方法で、ADO.Netを使用してDataTableをストアドプロシージャに渡すことができますが、データ型にいくつかの変更が必要です。通常、次のコードのように、varchar、nvarchar、intなどの通常のパラメーターにSqlParameterのDbTypeを指定します。

SqlParameter sqlParam= new SqlParameter();  
sqlParam.ParameterName = "@StudentName";  
sqlParam.DbType = DbType.String;  
sqlParam.Value = StudentName;  

ただし、テーブルパラメータの場合、パラメータデータタイプとしてDbTypeを指定する必要はありません。 DbTypeではなくSqlTypeを提供する必要があります。

SqlParameter Parameter = new SqlParameter;  
Parameter.ParameterName = "@PhoneBook";  
Parameter.SqlDbType = SqlDbType.Structured;  
Parameter.Value = PhoneTable;  

次の例では、電話帳のリストを受け取り、ADO.Netを使用してデータベースに保存します。この例では、リストから電話帳の詳細を取得してDataTableに格納し、このテーブルをパラメーターとしてNewPhoneBookという名前のストアドプロシージャに渡します。

//Phone book list    
List<PhoneBook> PhoneBooks    
 //CReating Table    
 DataTable PhoneTable = new DataTable();    

 // Adding Columns    
 DataColumn COLUMN=new DataColumn();    
 COLUMN.ColumnName="ID";    
 COLUMN.DataType= typeof(int);    
 PhoneTable.Columns.Add(COLUMN);    

 COLUMN = new DataColumn();    
 COLUMN.ColumnName = "ContactNumber";    
 COLUMN.DataType = typeof(string);    
 PhoneTable.Columns.Add(COLUMN);    

 COLUMN = new DataColumn();    
 COLUMN.ColumnName = "ContactName";    
 COLUMN.DataType = typeof(string);    
 PhoneTable.Columns.Add(COLUMN);    

 // INSERTING DATA    
 foreach (UserPhoneBook UPB in PhoneBooks)    
 {    
    DataRow DR = PhoneTable.NewRow();    
    DR[0] = UPB.UserName;    
    DR[1] = UPB.ContactNumber;    
    DR[2] = UPB.ContactName;    
    PhoneTable.Rows.Add(DR);    
 }    
 //Parameter declaration    
 SqlParameter[] Parameter = new SqlParameter[2];    
 Parameter[0].ParameterName = "@PhoneBook";    
 Parameter[0].SqlDbType = SqlDbType.Structured;    
 Parameter[0].Value = PhoneTable;    

 Parameter[1].ParameterName = "@Return_Value";    
 Parameter[1].Direction = ParameterDirection.ReturnValue;    
 //Executing Procedure  
 SqlHelper.ExecuteNonQuery(this.ConnectionString, CommandType.StoredProcedure, "[NewPhoneBook]", Parameter);

あなたはコードリファレンスを見つけることができます ここ

6
Ajay Pattni

これらのオブジェクトは使用しません。彼らは単にそのように役に立たないようです。そうは言っても、PostgreSQLはjsonbを介して関数にテーブルを渡すメソッドを持っています。 ストアドプロシージャへのテーブル値パラメーターの受け渡し で説明されている機能を取得したい場合は、それが理想的な解決策になるでしょう。 DataTableをJSONにマップして、JSONを関数に渡すだけです。

DatatableをJSONに変換するには、 JSON.net を使用できます。これらのスタックオーバーフローの回答もご覧ください。

Datatableが大きすぎてJSONにシリアル化できない場合は、DataRowオブジェクトをエクスポートするものの周りに外部データラッパーを作成する必要がある場合があります。これにより、 "DataReaderを使用したスト​​リーミング行" 。サーバー/クライアントモデルが必要ない場合は、いつでもDataTableをCSVにダンプし、 Foreign Data Wrapper :で読み取ることで、ルートチェックを行うことができます。 out file-fdw

実際に試してみました。私はたくさんの問題に遭遇しました。

  1. nuGet でパッケージをインストールする方法を理解するのも簡単ではありませんでした。
  2. NuGetをインストールした後、nuGetの#1パッケージであるにもかかわらず、 。NET Core 2.xでは動作しません
  3. betaバージョンのnuGet をインストールすると、動作しました。

.NET Core 2.xでDataTableをJSONに変換し、

_using System;
using System.Data;
using Newtonsoft.Json;

class Program
{
  static void Main()
  {
    // Get the DataTable.
    DataTable table = GetTable();
    // ... Use the DataTable here with SQL.
    string json = JsonConvert.SerializeObject(table, Formatting.Indented);
    Console.WriteLine(json);

  }

  static DataTable GetTable()
  {
    // Here we create a DataTable with four columns.
    DataTable table = new DataTable();
    table.Columns.Add("Dosage", typeof(int));
    table.Columns.Add("Drug", typeof(string));
    table.Columns.Add("Patient", typeof(string));
    table.Columns.Add("Date", typeof(DateTime));

    // Here we add five DataRows.
    table.Rows.Add(25, "Indocin", "David", DateTime.Now);
    table.Rows.Add(50, "Enebrel", "Sam", DateTime.Now);
    table.Rows.Add(10, "Hydralazine", "Christoff", DateTime.Now);
    table.Rows.Add(21, "Combivent", "Janet", DateTime.Now);
    table.Rows.Add(100, "Dilantin", "Melanie", DateTime.Now);

    return table;
  }
}
_

それでもデータベースに接続してjsonをINSERTステートメントにドロップする必要がありますが、その は簡単に実行できるはずです。

_cmd.CommandText = "SELECT * FROM myfunc(@json)";
cmd.Parameters.AddWithValue("json", json);
_

サーバーでmyfunc(jsonb)を定義すると、PostgreSQLにDataTableが渡されます。

5
Evan Carroll

SQL Serverでできるのと同じ方法でPostgreSQLでそれを行うことができるとは思いません。とはいえ、内部で何が起こっているかを見ると、実際にシミュレーションすることはそれほど難しくありません。よくある誤解は、データテーブルがストアドプロシージャに「全体」渡されるというものです。そうではありません。データテーブルは、SQL Server側でデータテーブル内のデータを使用して再構築されます。 SQL Serverでこのアクションを実行すると、実際には次のようになります。

  • 指定されたテーブルタイプの変数がメモリに作成されます。
  • データテーブルの各レコードの変数に挿入が行われます。
  • 変数への参照がストアドプロシージャに渡されます。

次のようになります。

DECLARE @p1 [dbo].[table_type_name];
INSERT INTO @p1 VALUES(...);
INSERT INTO @p1 VALUES(...);
-- continue until all rows have been inserted
EXEC [dbo].[sp_something] @dataTable = @p1;

何も特別なことはありません。一時テーブルまたはステージングテーブルを使用してこれをシミュレートし、SQLを自分で生成するのは簡単です。唯一の大きな違いは、テーブルへの参照をストアドプロシージャに渡すことができないことです。

Evanが述べたように、PostgreSQLにデータを渡す方法は他にもありますが、クロスプラットフォームソリューションを探している場合は、ANSI準拠のメソッドのみを選択する必要があります-残念ながら、これにはSQL Serverテーブル値パラメーターが含まれていません。

これは、ある種のステージングテーブルを必要とするソリューションを実装する方法です。

  • セッションIDを返すために使用できるシーケンスを実装する
  • 必要な列とセッションIDの列を含むステージングテーブルを作成します
  • 指定されたセッションIDと一致するステージングテーブルからデータを取り出し、それをターゲットテーブルとマージするストアドプロシージャを作成します。

例:

[〜#〜] sql [〜#〜]

CREATE SEQUENCE seq_get_session_id START 1;

CREATE TABLE stg_table (
   session_id bigint not null,
   -- remainder of columns
);

CREATE OR REPLACE FUNCTION insert_data(_session_id BIGINT)
RETURNS void AS
$BODY$
BEGIN
    INSERT INTO my_table (...)
    SELECT /* columns */
      FROM stg_table
     WHERE session_id = _session_id;

    DELETE FROM stg_table
    WHERE session_id = _session_id;
END
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;

C#

var phonebooks = GetPhoneBooks();

using (var connection = GetConnection())
{
    long sessionId;
    connection.Open();
    var transaction = connection.BeginTransaction();

    // get the session id
    using(var command = connection.CreateCommand())
    {
        command.Transaction = transaction;
        command.CommandText = "SELECT nextval('seq_get_session_id')";
        using (var reader = command.ExecuteReader())
        {
            if (!reader.Read())
                throw new InvalidOperationException("Unable to obtain session id");
            sessionId = reader.GetInt64(0);
        }       
    }

    // insert data into staging table
    using (var insertCommand = connection.CreateCommand())
    {
        insertCommand.CommandText = 
            "INSERT INTO stg_table (session_id, ...)" + 
            " VALUES (_session_id, ...)";
        insertCommand.Transaction = transaction;
        insertCommand.Parameters.AddWithValue("_session_id", sessionId);

        var p1 = insertCommand.CreateParameter();
        p1.ParameterName = "_p1";
        p1.DbType = DbType.DateTime;
        p1.Value = DateTime.Now;
        insertCommand.Parameters.Add(p1);

        // repeat above for each property that needs to be persisted...

        foreach(var phonebook in phonebooks)
        {
            p1.Value = phonebook.Date;
            // set values for any other parameters you have declared...

            insertCommand.ExecuteNonQuery();
        }    
    }

    // execute final stored procedure to merge the data
    using (var insertDataCommand = connection.CreateCommand())
    {
        insertDataCommand.CommandText = "insert_data";
        insertDataCommand.CommandType = CommandType.StoredProcedure;
        insertDataCommand.Parameters.AddWithValue("_session_id", sessionId);

        insertDataCommand.Transaction = transaction;
        insertDataCommand.ExecuteNonQuery();
    }  

    transaction.Commit();
}

ただし、ステージングテーブルが必要ない場合、上記はmajor過剰です。ターゲットテーブルにデータを直接挿入できる場合は、上記のコードのほとんどを省略でき、これを行うだけです:

using (var connection = GetConnection())
{
    connection.Open();
    var transaction = connection.BeginTransaction();

    using (var insertCommand = connection.CreateCommand())
    {
        insertCommand.CommandText = 
            "INSERT INTO my_table (...)" + 
            " VALUES (...)";
        insertCommand.Transaction = transaction;

        var p1 = insertCommand.CreateParameter();
        p1.ParameterName = "_p1";
        p1.DbType = DbType.DateTime;
        p1.Value = DateTime.Now;
        insertCommand.Parameters.Add(p1);

        // repeat above for each property that needs to be persisted...

        foreach(var phonebook in phonebooks)
        {
            p1.Value = phonebook.Date;
            // set values for any other parameters you have declared...

            insertCommand.ExecuteNonQuery();
        }         
    }

    transaction.Commit();
}
3
Mr.Brownstone

合格方法

データ表

POSTGRESQLのパラメーターとして

=================================================

ステップ1

POSTGREでデータテーブルとして渡すタイプテーブルを作成します

例…

私はpostgresqlで学生タイプを作成します

Type Student As(id integer、name text);

-==テスト用に物理テーブルを作成します。テーブルティーチャー(ID整数、名前テキスト)を作成します。

ステップ2

CREATE OR REPLACE FUNCTION public.usp_InsertStudent(ref1 refcursor、p_str_string json)RETURNS refcursor AS $ BODY $ BEGIN

Insert into  Teacher (id,name)
select id,name from json_populate_recordset(null::Student,p_str_string);

OPEN REF1 FOR
select * from  Teacher;

return  ref1;

終わり; $ BODY $ LANGUAGE plpgsql VOLATILE COST 100;

ステップ:-3

C#からの呼び出し

DataSet ds = new DataSet(); DataTable table = new DataTable(); table.Columns.Add( "id"、typeof(int)); table.Columns.Add( "name"、typeof(string)); table.Rows.Add(25、 "ajay"); table.Rows.Add(25、 "rajesh"); table.Rows.Add(25、 "jayesh");

        string json = JsonConvert.SerializeObject(table, Formatting.Indented);
        string conStr = "put connection string here..";
        NpgsqlConnection conn = new NpgsqlConnection(conStr);
        conn.Open();
        string query = string.Empty;
        query = "select * from usp_InsertStudent('ref1'";
        query = query + ",p_str_string:='"+json+"'"+
         ");fetch all in " + "\"ref1\";";

         NpgsqlDataAdapter da = new NpgsqlDataAdapter(query, conn);
         da.Fill(ds);
         System.Data.DataTable dt = ds.Tables[1];
         //// connect grid to DataTable
         dataGridView1.DataSource = dt;
         dataGridView1.DataBind();
0
Ajay Pattni