C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: データベースからのデータの取得方法を教えてください。


(過去ログ 143 を表示中)

[トピック内 9 記事 (1 - 9 表示)]  << 0 >>

■84173 / inTopicNo.1)  データベースからのデータの取得方法を教えてください。
  
□投稿者/ 夜叉丸 (57回)-(2017/05/25(Thu) 15:35:47)

分類:[.NET 全般] 

データベースで以下の2列を作成し、ネットを調べて Byte[] のデータを登録しました。

NAME nvarchar(256) null以外
DATA varbinary(max) null以外

Byte[] data = { 0x01, 0x02, 0x03 };

string str = "0x";
for(int idnum = 0; idnum < data.Length; idnum++) str += data[idnum].ToString("X2");
connection.Open();
command.CommandText = "INSERT INTO SIMS_SZ5G.dbo.M_COMMONFILE ( NAME, DATA ) VALUES ('test', " + str + ")";
command.ExecuteNonQuery();
で登録

SELECT NAME, DATA
FROM M_COMMONFILE

connection.Open();
command.CommandText = "SELECT NAME, DATA FROM M_COMMONFILE";
var adapter = new SqlDataAdapter(command);
adapter.Fill(table);
で取得しようとしたのですが

取得できませんでした。
どうやって取得するのでしょうか?

引用返信 編集キー/
■84174 / inTopicNo.2)  Re[1]: データベースからのデータの取得方法を教えてください。
□投稿者/ WebSurfer (1243回)-(2017/05/25(Thu) 15:59:27)
No84173 (夜叉丸 さん) に返信

> command.CommandText = "INSERT INTO SIMS_SZ5G.dbo.M_COMMONFILE ( NAME, DATA ) VALUES ('test', " + str + ")";

↑ ここがダメだと思います。リテラルをパラメータ化してパラメータにバイト列を代入しては
いかがですか?


引用返信 編集キー/
■84175 / inTopicNo.3)  Re[1]: データベースからのデータの取得方法を教えてください。
□投稿者/ 魔界の仮面弁士 (1289回)-(2017/05/25(Thu) 16:14:40)
No84173 (夜叉丸 さん) に返信
> string str = "0x";
> for(int idnum = 0; idnum < data.Length; idnum++) str += data[idnum].ToString("X2");

string 型を += で繋げていますが、データ量が長くなる可能性がある場合は、
StringBuilder で連結した方が良いでしょう。

また、上記はループ処理せずとも、
 string str = "0x" + BitConverter.ToString(data).Replace("-", "");
とも書くこともできます。



> connection.Open();
> command.CommandText = "SELECT NAME, DATA FROM M_COMMONFILE";
> var adapter = new SqlDataAdapter(command);
> adapter.Fill(table);
> で取得しようとしたのですが
> 取得できませんでした。

当方では、下記のコードで取得されました。

var table = new DataTable();
using (var connection = new SqlConnection(接続文字列))
using (var command = connection.CreateCommand())
{
  connection.Open();
  command.CommandText = "SELECT NAME, DATA FROM M_COMMONFILE";
  using (var adapter = new SqlDataAdapter(command))
  {
    adapter.Fill(table);
  }
  connection.Close();
}

string sName = "";
byte[] bData = new byte[0];
if (table.Rows.Count > 0)
{
  sName = table.Rows[0]["NAME"] as string ?? "";
  bData = table.Rows[0]["DATA"] as byte[] ?? new byte[0];
}
引用返信 編集キー/
■84176 / inTopicNo.4)  Re[2]: データベースからのデータの取得方法を教えてください。
□投稿者/ Hongliang (546回)-(2017/05/25(Thu) 16:15:32)
「取得できませんでした」とありますが、
・実行時に例外になる
・DataTableにINSERTした行が存在しない
・DataTableに格納されている値が期待したものと異なる
・その他
どれでしょうか?
引用返信 編集キー/
■84177 / inTopicNo.5)  Re[1]: データベースからのデータの取得方法を教えてください。
□投稿者/ WebSurfer (1244回)-(2017/05/25(Thu) 17:39:32)
No84173 (夜叉丸 さん) に返信

先の私のレスで、

> リテラルをパラメータ化してパラメータにバイト列を代入してはいかがですか?

と書きましたが、具体例を書いておきます。

テーブルは自分の PC の SQL Server にある既存のものを使ったので質問者さんのテーブル
とは少々異なり、以下の通りです。

CREATE TABLE [dbo].[Files](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[FileName] [nvarchar](100) NOT NULL,
	[MimeType] [nvarchar](50) NOT NULL,
	[BinaryData] [varbinary](max) NOT NULL,
	[DownLoadCount] [int] NOT NULL,
 CONSTRAINT [PK__Files] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

varbinary(max) を使うということは、そのフィールドに画像とか pdf などのファイルを保存
するのだと思いますが、そうだとすると、下のコードの new byte[] { 0x01, 0x02, 0x03 } を
ファイルを読み込んで作ったバイト列にすればいいです。

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string connStr = "Data Source=(local)\\sqlexpress;Initial Catalog=UserDatabase;Integrated Security=true";
            string insertQuery = "INSERT INTO [Files] ([FileName], [MimeType], [BinaryData], [DownloadCount]) " +
                                 "VALUES (@filename, @mimetype, @binarydata, @downloadcount)";

            using (SqlConnection connection = new SqlConnection(connStr))
            {
                using (SqlCommand command = new SqlCommand(insertQuery, connection))
                {
                    command.Parameters.AddWithValue("@filename", "ファイル名");
                    command.Parameters.AddWithValue("@mimetype", "タイプ");
                    command.Parameters.AddWithValue("@binarydata", new byte[] { 0x01, 0x02, 0x03 });
                    command.Parameters.AddWithValue("@downloadcount", 0);

                    connection.Open();
                    command.ExecuteNonQuery();
                }
            }

            string selectQuery = "SELECT [FileName], [MimeType], [BinaryData], [DownloadCount] FROM [Files]";

            using (SqlConnection connection = new SqlConnection(connStr))
            {
                using (SqlCommand command = new SqlCommand(selectQuery, connection))
                {
                    connection.Open();
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            string filename = (string)reader[0];
                            string mimetyoe = (string)reader[1];
                            byte[] binarydata = (byte[])reader[2];
                            int downloadcount = (int)reader[3];
                        }
                    }
                }
            }
        }
    }
}

引用返信 編集キー/
■84178 / inTopicNo.6)  Re[1]: データベースからのデータの取得方法を教えてください。
□投稿者/ shu (1023回)-(2017/05/26(Fri) 07:01:46)
No84173 (夜叉丸 さん) に返信

提示内容ではcommandにconnectionが設定されていませんが
大丈夫でしょうか?
引用返信 編集キー/
■84183 / inTopicNo.7)  Re[1]: データベースからのデータの取得方法を教えてください。
□投稿者/ 魔界の仮面弁士 (1290回)-(2017/05/26(Fri) 09:59:03)
No84173 (夜叉丸 さん) に返信
> 取得できませんでした。

現状は、INSERT INTO はエラー無く実行されたけれど、
SELECT が期待動作していない状況ということでしょうか。

INSERT と SELECT は続けて行っていますか?
それとも INSERT 後にアプリを終了し、アプリの再起動後に SELECT していますか?


もし、INSERT 直後であれば SELECT で結果を参照できるのに、
再接続後に SELECT すると、先の 内容が見れなくなっているようであれば、
登録したデータがロールバックされているのかも知れません。


(可能性 1) mdf ファイルをローカルにアタッチしており、更新対象のファイルが異なっていた。
 http://www.atmarkit.co.jp/fdotnet/dotnettips/532dbfilecopy/dbfilecopy.html


(可能性 2) connection.BeginTransaction を呼び出したのに、
SqlTransaction.Commit メソッドを呼びだすのを忘れていた。


前者については、mdf をアタッチしている場合に限った問題です。
別サーバーに接続している場合は大丈夫でしょう。

後者については、そもそも最初に提示されたコードに、明示的トランザクションの記述が
無かったので、可能性としては低そうです。



> command.CommandText = "INSERT INTO SIMS_SZ5G.dbo.M_COMMONFILE ( NAME, DATA ) VALUES ('test', " + str + ")";

構文的には問題ないのですが、上記の登録クエリーでは SIMS_SZ5G データベースの
M_COMMONFILE テーブルへの追記が行われているのに対し、

> command.CommandText = "SELECT NAME, DATA FROM M_COMMONFILE";

の選択クエリーでは、FROM SIMS_SZ5G.dbo.M_COMMONFILE とはなっておらず、
FROM M_COMMONFILE テーブルとなっている点が気にかかりました。
https://msdn.microsoft.com/ja-jp/library/bb669061.aspx


現在のコンテキストが、想定通りのデータベース(SIMS_SZ5G)を指しているかどうか、
もういちど確認してみてください。


(1) SQL Server Management Studio 等を通じて接続し、SQL Server 側の
  セキュリティ設定を開いて、アプリで利用しているログインアカウントに対する
 "既定のデータベース" が何に割り当てられているかを確認してみてください。
 (たとえば管理アカウントだと、master データベースが既定になっていたりします)


(2) 接続文字列に Initial Catalog=SIMS_SZ5G; が付与されているか確認してみてください。
 Initial Catalog の指定が SIMS_SZ5G 以外を指していたりはしませんか?
 なお、Initial Catalog の指定が行われていなかった場合の接続先は
 (1) で設定されている既定のデータベースに接続されるはずです。


(3) 目的の SELECT クエリーを発行する前に、USE コマンドが呼び出されてはいませんか?
 もしも (2) で正しいコンテキストが設定されていたとしても、USE コマンドによって
 カレントコンテキストが別のデータベースを参照されるケースがあります。
 (あるいは逆に、SELECT する前に "USE SIMS_SZ5G;" を呼んでおくという手もありますが)


(4) 上記いずれも直ちに確認できない場合は、
 ひとまず、"SELECT NAME, DATA FROM M_COMMONFILE" の部分を
 "SELECT NAME, DATA FROM SIMS_SZ5G.dbo.M_COMMONFILE" に変更して
 正しく読み込めるかどうかを確認してみてください。
引用返信 編集キー/
■84184 / inTopicNo.8)  Re[1]: データベースからのデータの取得方法を教えてください。
□投稿者/ WebSurfer (1245回)-(2017/05/26(Fri) 10:51:26)
No84173 (夜叉丸 さん) に返信

パラメータ化の重要性について追加情報を書いておきます。特に (1) が重要で、パラメ
ータ化クエリは必ず利用すべきと自分は思います。

(1) SQL インジェクション防止

パラメータ化すれば、パラメータの入力はリテラルとして扱われるので SQL インジェク
ション攻撃を防ぐことができます。詳しくは以下の記事を見てください。

パラメータ化クエリ
http://surferonwww.info/BlogEngine/post/2012/02/02/Parameterized-query.aspx


(2) パフォーマンスの向上

多数のレコードを繰り返し INSERT するような場合はパラメータ化クエリを使えば実行
プランの再利によってパフォーマンスの向上が期待できます。

(今回の例のように INSERT クエリの発行が一回限りならパフォーマンスの向上は望め
ませんが)


(3) 文字化け防止

パラメータ化の副次的な効用として文字化け防止があります。

例えば、SQL Azure の照合順序のデフォルトは SQL_Latin1_General_CP1_CI_AS となっ
ているのですが、それに以下のように N プレフィックスをつけないリテラルを INSERT
すると文字化けします。

INSERT INTO [Table] ([Name]) VALUES ('あいうえお')

詳しくは以下の記事を見てください。

パラメータ化の副次的な効用
http://surferonwww.info/BlogEngine/post/2016/06/04/use-parameterized-query-to-avoid-unexpected-character-corruption.aspx

(パラメータ化が文字化け対策と言っているのではありません。クエリをパラメータ化
して ADO.NET + SqlClient を使うという当たり前のことをしておけば、照合順序の違い
によって文字化けに悩むことはなさそうと言っています)
引用返信 編集キー/
■84192 / inTopicNo.9)  Re[2]: データベースからのデータの取得方法を教えてください。
□投稿者/ 夜叉丸 (59回)-(2017/05/29(Mon) 18:22:34)
みなさんありがとうございます。

結局、VarBinary は難しくて私の手には負えず、できませんでした。

最終的に varchar(max) に変更して

string str = "";
for(int idnum = 0; idnum < data.Length; idnum++) str += data[idnum].ToString("X2");

を保存して
for(int inum = 0; inum < text.Length; inum += 2) data[inum / 2] = Convert.ToByte(text.Substring(inum, 2), 16);

データ量が増えてしまいますが、このような形になってしまいました。

もう少し勉強して varbinary を使えるように頑張ります。


解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -