SqlBulkCopy のスピードは 20 倍ぐらい早い | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2234
なところで、SQL Server へのインサートを高速化したのですが、実はちょっとした落とし穴があります(落とし穴、というよりも「仕様」っぽいのですが)。
SQL Server で Decimal 型を使うと、Oracle の nameric 型のように精度を指定できます。たとえば decimal(18,4) と設定すると、小数点以下第4桁までの精度になります。
この場合、「0.12345」という値を入れようとすると、クエリでは「0.1235」のように最後の桁で四捨五入されるのですが、SqlBulkCopy を使うと「0.1234」のように切り捨てになる、という違いがあります。
CREATE TABLE [dbo].[t_dec]( [dec] [decimal](18, 4) NULL, [dbl] [float] NULL ) ON [PRIMARY]
のようにテーブルを作成しておいて、データベースに書き込みます。
''' <summary> ''' BulkCopy で書き込み ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click Dim dec As Decimal = 0.1234567890123456789012345679D Dim dbl As Decimal = 0.12345678901234559 Dim dt As New DataTable dt.Columns.Add("dec", GetType(Decimal)) dt.Columns.Add("dbl", GetType(Double)) Dim row As DataRow = dt.NewRow row("dec") = dec row("dbl") = dbl dt.Rows.Add(row) Dim cn As New SqlConnection(CNSTR) Dim bc As New SqlBulkCopy(cn) cn.Open() bc.DestinationTableName = "t_dec" bc.WriteToServer(dt) cn.Close() bc.Close() ' decimal(18,4) で指定していると、dec = 0.1234 となり切り捨てとなる Dim dt2 As New DataTable Dim da As New SqlDataAdapter("SELECT * FROM t_dec", cn) da.Fill(dt2) DataGridView1.DataSource = dt2 End Sub ''' <summary> ''' SqlCommand で書き込み ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click Dim dec As Decimal = 0.1234567890123456789012345679D Dim dbl As Decimal = 0.12345678901234559 Dim cn As New SqlConnection(CNSTR) Dim cmd As New SqlCommand("INSERT INTO t_dec values ( @DEC, @DBL )", cn) cmd.Parameters.Add(New SqlParameter("@DEC", dec)) cmd.Parameters.Add(New SqlParameter("@DBL", dbl)) cn.Open() cmd.ExecuteNonQuery() cn.Close() ' decimal(18,4) で指定していると、dec = 0.1235 となり四捨五入される Dim dt2 As New DataTable Dim da As New SqlDataAdapter("SELECT * FROM t_dec", cn) da.Fill(dt2) DataGridView1.DataSource = dt2 End Sub
こうすると、SqlCommand の場合は「0.1235」のように四捨五入されるのですが、SqlBulkCopy の場合は「0.1234」のように切り捨てられます。
なので、仕方がないので(?)、四捨五入されるように「0.00005D」を加算します(末尾の「D」は、decimal 型の印です)
''' <summary> ''' decimal の精度に合わせて 0.00005 を加算する ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub Button5_Click(sender As System.Object, e As System.EventArgs) Handles Button5.Click Dim dec As Decimal = 0.1234567890123456789012345679D Dim dbl As Decimal = 0.12345678901234559 Dim dt As New DataTable dt.Columns.Add("dec", GetType(Decimal)) dt.Columns.Add("dbl", GetType(Double)) Dim row As DataRow = dt.NewRow row("dec") = dec + 0.00005D ' 四捨五入させる row("dbl") = dbl dt.Rows.Add(row) Dim cn As New SqlConnection(CNSTR) Dim bc As New SqlBulkCopy(cn) cn.Open() bc.DestinationTableName = "t_dec" bc.WriteToServer(dt) cn.Close() bc.Close() ' BulkCopy だと切り捨てになるので、 ' decimal(18,4) の場合は 0.00005D を加えて、 ' dec = 0.1235 のように四捨五入にする Dim dt2 As New DataTable Dim da As New SqlDataAdapter("SELECT * FROM t_dec", cn) da.Fill(dt2) DataGridView1.DataSource = dt2 End Sub
さて、これを組み込みかどうかを思案中。