資料表
USE [MyLab] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Product]( [id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Product_id] DEFAULT (newid()), [name] [nvarchar](50) NOT NULL, [descrp] [nvarchar](max) NULL, [lastEditAt] [datetime] NOT NULL CONSTRAINT [DF_Product_lastEditAt] DEFAULT (getdate()), CONSTRAINT [PK_Product] 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] TEXTIMAGE_ON [PRIMARY] GO
建使用者定義資料表類型
USE [MyLab] GO CREATE TYPE [dbo].[Product] AS TABLE( [id] [uniqueidentifier] NOT NULL, [name] [nvarchar](50) NOT NULL, [descrp] [nvarchar](max) NULL, [lastEditAt] [smalldatetime] NOT NULL ) GO
預存程序(建之前 SSMS 要重連,不然似乎會抓不到上一步建的資料表類型)
USE [MyLab] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE Procedure [dbo].[sp_BatchEditProd] ( @fromClient Product readonly, @opMsg nvarchar(MAX) output ) As UPDATE Product SET name = cli.name, descrp = cli.descrp, lastEditAt = GETDATE() FROM Product srv INNER JOIN @fromClient cli ON srv.id = cli.id GO
建測試資料
private static string BatchInsertProd(ListlstOfProd) { List lst = new List (); for (int i = 0; i < 20000; i++) { Product prod = new Product(); prod.id = Guid.NewGuid(); prod.name = MyExts.GetRandomString(6); prod.descrp = MyExts.GetRandomString(25); prod.lastEditAt = DateTime.Now; lst.Add(prod); } db.Product.AddRange(lst); db.SaveChanges(); return string.Empty; }
亂數
public static string GetRandomString(int len) { var str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; var rand = new Random(Guid.NewGuid().GetHashCode()); var builder = new StringBuilder(); for (var i = 0; i < len; i++) { builder.Append(str[rand.Next(0, str.Length)]); } return builder.ToString(); }
最後就是使用 Stopwatch 比較兩個方案的耗時
SaveChanges
private static void UpdateProd(ListlstOfProd) { StirData(lstOfProd); sw.Start(); db.SaveChanges(); sw.Stop(); Console.WriteLine("UpdateProd m-sec: " + sw.ElapsedMilliseconds); }
Solution
private static string BatchUpdateProd(ListlstOfProd) { StirData(lstOfProd); sw.Start(); string sqlCmd = "sp_BatchEditProd @fromClient, @opMsg out"; string outMsg = string.Empty; var dt = lstOfProd.ToDataTable(); List lstOfPars = new List () { new SqlParameter() { Direction = ParameterDirection.Input, ParameterName = "fromClient", SqlDbType = SqlDbType.Structured, TypeName = "Product", Value = dt }, new SqlParameter() { Direction = ParameterDirection.Output, ParameterName = "opMsg", SqlDbType = SqlDbType.NVarChar, Size = 40, Value = null } }; db.Database.ExecuteSqlCommand(sqlCmd, lstOfPars.ToArray()); sw.Stop(); Console.WriteLine("BatchUpdateProd m-sec: " + sw.ElapsedMilliseconds); return outMsg; }
其中,StirData() 會製造 20000筆資料異動,與重設 Stopwatch
private static void StirData(ListlstOfProd) { sw.Reset(); for (int i = 0; i < 20000; i++) { Product prod = lstOfProd[i]; prod.name = MyExts.GetRandomString(6); prod.descrp = MyExts.GetRandomString(25); prod.lastEditAt = DateTime.Now; } }
最後就是結果比較
ref: Yun-Long Lin - 使用資料表值參數進行資料的批次作業
沒有留言:
張貼留言