• ベストアンサー

テキストファイルのデータをPostgreSQLに書き込む方法を教えてください

テキストファイルから読み込んだデータをテーブルに書き込むCプログラムを作りたいのですが PostgreSQL 8.3.1を学び始まったばかりです。 テキストファイル(ユーザ、姓、名)と言う項目を作り、 データを<user>テーブルに書き込むプログラムを作りたいのですが、 「PostgreSQL 8.3.1文書 第 30章libpq - C ライブラリ」を読んでも、 どこからやればいいか、まったくわかりません。 お分かりの方、是非教えてください

質問者が選んだベストアンサー

  • ベストアンサー
  • Dodonpa2
  • ベストアンサー率82% (19/23)
回答No.3

こんにちは。 >VALUES("ここはどういう感じで書けばいいですか?”) 行で抽出したdataを区切り文字(Delimiter)で分割して、各値をカンマ(,)で 区切って記述します。(動的にSQLステートメントを生成する場合) >* この「data」は一行単位ですが、どういう形式でテーブルにINSERT INTO できますか? 数値型でしたらDelimiterで分割するだけでいいですが、 文字列でしたら、単一引用符(')をつけないといけないです。 >* VALUES の値は必ず、具体的な値ではなければなりませんか? 動的にSQLステートメントを生成する場合はそうですが、先に申したように 文字列は単一引用符(')で囲まなければならないなどややこしいので、 パラメータを使用できるPQexecParamsを使うと、値はパラメータに渡せば いいので楽になるかもしれません。 以下にPQexecParamsの例を記述します。(VC++ MFC) //テキストファイル(カンマ区切り)のPostgreSQLテーブルへのインポート //引数 pszFileName : テキストファイルへのフルパス // pszTblName : テーブル名 1>int CopyFromString(TCHAR *pszFileName, TCHAR *pszTblName) 2>{ 3> //接続/////////////////////////////////////////////////////////// 4> PGresult *pRes; 5> TCHAR *pCnInfo = _T("host=xxx port=5432 dbname=xxx user=xxx password=xxx"); 6> PGconn *pCn = PQconnectdb(pCnInfo); 7> 8> if (PQstatus(pCn) != CONNECTION_OK) 9> { 10> TRACE(_T("Fail Connect Server\n")); 11> PQfinish(pCn); 12> return 1; 13> } 14> 15> //Table フィールド数取得///////////////////////////////////////// 16> CString strCmd; //SQLステートメント 17> int column_count; //フィールド数 18> 19> strCmd = _T("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = $1;"); 20> pRes = PQexecParams(pCn, strCmd, 1, NULL, &pszTblName, NULL, NULL, 0); 21> 22> if (PQresultStatus(pRes) != PGRES_TUPLES_OK) 23> { 24> TRACE(_T("Fail Get Column:%s\n"), PQresultErrorMessage(pRes)); 25> PQclear(pRes); 26> PQfinish(pCn); 27> return 1; 28> } 29> column_count = _tstoi(PQgetvalue(pRes, 0, 0)); 30> PQclear(pRes); 31> 32> //テキストファイルOpen////////////////////////////////////////// 33> CStdioFile file; 34> CFileException e; 35> if (!file.Open(pszFileName, CFile::modeRead | 36> CFile::shareDenyWrite | 37> CFile::osSequentialScan, &e) 38> ) 39> { 40> CString strErr; 41> e.GetErrorMessage(strErr.GetBuffer(255), 255); 42> strErr.ReleaseBuffer(); 43> TRACE(_T("File Open Error:%s\n"), strErr) ; 44> return 1; 45> } 46> 47> //メイン///////////////////////////////////////////////////////// 48> int nRet = 0; 49> int start; 50> int idx; 51> CString strData; //Text行データ 52> CString strTemp; //SQLステートメント補助 53> CString *pstrItem = new CString[column_count]; //行データ各項目 54> 55> //SQL(INSERT)ステートメント生成 56> strCmd = _T(""); 57> for (int i=0; i<column_count; i++) 58> { 59> strTemp.Format(_T(",$%d"), i+1); 60> strCmd += strTemp; 61> } 62> // INSERT INTO tblname VALUES ($1, $2, $3, $4); 63> strCmd = _T("INSERT INTO ") + CString(pszTblName) + _T(" VALUES (") + strCmd.Mid(1) + _T(");"); 64> 65> //データ読込み & テーブルへの書込み 66> while (file.ReadString(strData)) 67> { 68> //Text行の読込み & データ項目取得 69> start = 0; 70> idx = 0; 71> for (int i=0; i<column_count; i++) 72> { 73> idx = strData.Find(_T(","), start); //Delimiter "," 74> *(pstrItem + i) = strData.Mid(start, idx == -1 ? strData.GetLength() : idx-start); 75> start = ++idx; 76> } 77> 78> //Tableへの書込み 79> pRes = PQexecParams(pCn 80> , strCmd 81> , column_count 82> , NULL 83> , reinterpret_cast<const char * const *>(pstrItem) 84> , NULL 85> , NULL 86> , 1); 87> if (PQresultStatus(pRes) != PGRES_COMMAND_OK) 88> { 89> TRACE(_T("Fail Insert Data:%s\n"), PQresultErrorMessage(pRes)); 90> nRet = 1; 91> } 92> 93> //後始末 94> for (int i=0; i<column_count; i++) 95> { 96> *(pstrItem + i) = _T(""); 97> } 98> PQclear(pRes); 99> } 100> 101> delete [] pstrItem; 102> file.Close(); 103> PQfinish(pCn); 104> 105> return nRet; 106>} この場合でもテキストの一行の データをDelimiterで分割しなければなりません。53でフィールド数分データ格納用の 領域を確保して69~76で分割しています。前コメントでも申しましたように VC++ MFCのCStringで楽してるので、Cでしたらこの部分をmalloc、callocで領域を 確保してstring.hをインクルードしてstrncpyなどを使って分割することになりますが、 時間があればアップします。

sonk0001
質問者

お礼

御掛け様で、上手く動きました。 本当に良いお勉強になって、ありがとうございました。 またよろしくお願いいたします。

その他の回答 (2)

  • Dodonpa2
  • ベストアンサー率82% (19/23)
回答No.2

こんにちは。 >[COPY] 以外の方法はありませんか?[INSERT**] で、一行づつ読んで、格納して、テーブルに書き込む >方法ありませんか? Cでしたら、fopenでテキストを開いてgetsで読み込んで 1レコードずつINSERTをPQexecなどで実行していけばできるでしょう。

sonk0001
質問者

お礼

Dodonpa2 様、  こんにちは。  ご返事、誠にありがとうございます。 愚痴の問題ですが、もう一つ聞きたいのです。 while( fgets( data , S_MAX , fp ) != NULL ){ ++line ; printf( "%s" , data); INSERT INTO testdb (code , fname ,lname ,del) VALUES("ここはどういう感じで書けばいいですか?”) * この「data」は一行単位ですが、どういう形式でテーブルにINSERT INTO できますか? * VALUES の値は必ず、具体的な値ではなければなりませんか?

  • Dodonpa2
  • ベストアンサー率82% (19/23)
回答No.1

こんにちは。 >どこからやればいいか、まったくわかりません。 COPY FROMを使う例です。 (接続文字列をハードコーディングしてますが、わかりやすく するためにやっているだけなので避けるようにしてください) VC++ (MFC)の環境なのでちょっと違いますが、 雰囲気は伝わるかと思います。 また、テキストの状況が書かれていないので カンマ区切りのテキストとして処理しています。 #include "libpq-fe.h" #pragma comment(lib, "libpq.lib") using namespace std; int CopyFromText(TCHAR *pszFileName); int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { _tprintf(_T("致命的なエラー: MFC の初期化ができませんでした。\n")); nRetCode = 1; } else { if (argc > 1) { int nRetCode = CopyFromText(argv[1]); //COPY FROMコマンド実行 } else { nRetCode = 1; } } return nRetCode; } int CopyFromText(TCHAR *pszFileName) { int nRet = 0; int length; CString strCmd; CString strFileName; CString strEscapeFileName; PGresult *pRes; TCHAR *pCnInfo = _T("host=サーバ port=5432 dbname=DB名 user=postgres password=xxx"); PGconn *pCn = PQconnectdb(pCnInfo); //接続 if (PQstatus(pCn) != CONNECTION_OK) { TRACE(_T("Fail Connect Server\n")); PQfinish(pCn); return 1; } //ファイルパスエスケープ処理 strFileName = pszFileName; length = strFileName.GetLength(); PQescapeString(strEscapeFileName.GetBuffer(length*2), strFileName, length); strEscapeFileName.ReleaseBuffer(); //COPY FROMコマンド実行 strCmd = _T("COPY テーブル名 FROM '") + strEscapeFileName + _T("' DELIMITER ',';"); pRes = PQexec(pCn, strCmd); if (PQresultStatus(pRes) != PGRES_COMMAND_OK) { TRACE(_T("Fail COPY FROM:%s\n"), PQresultErrorMessage(pRes)); nRet = 1; } PQclear(pRes); PQfinish(pCn); return nRet; }

sonk0001
質問者

お礼

おかけ様で、うまく動きました。 どうもありがとうございました。 [COPY] 以外の方法はありませんか?[INSERT**] で、一行づつ読んで、格納して、テーブルに書き込む方法ありませんか?

関連するQ&A