圖1 |
-- ================================ -- Create User-defined Table Type -- ================================ USE projmng GO -- Create the data type CREATE TYPE dbo.BKTable AS TABLE ( TID int NOT NULL, TName nvarchar(50) NULL, PRIMARY KEY (TID) ) GO
在目前範例中,我建立了一個名為 BKTable 的自訂資料表類型,有兩個欄位 TID 與 TName ,其中的 TID 是 PKey。
declare @TB BKTable insert into @TB values (1,'Paladin'),(2,'Hugo') select * from @TB建立完我們自訂的資料表類型後,我好奇地先宣告了一個 BKTable 的變數 @TB 。把它視為是一個 Table ,所以試著把些資料新增進去,再顯示 @TB 的結果,就如(圖2)所示,跟以前使用資料表的方式幾乎一樣。
圖2 |
為了方便說明,建立了一個實體資料表 TB ,以便待會測試時使用。
/*------------------------------------- create DataTable: TB -------------------------------------*/ CREATE TABLE [dbo].[TB]( [tID] [int] IDENTITY(1,1) NOT NULL, [tName] [nvarchar](50) NULL, [tKeyin] [nvarchar](20) NULL, CONSTRAINT [PK_TB] PRIMARY KEY CLUSTERED ( [tID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO
接著建立一個 Store Procedure : pr_tb_insert ,用來解釋如何使用 TVP 當作參數傳入 Store Procedure。在下面這段程式裡,傳入參數 @tb 正是我們自訂資料表類型 (BKTable),唯一特別的地方,是要在參數後方加註 READONLY ,也就是說,針對這個參數 @tb 所指定的資料表內容,就不可以再進行 Insert , Update , Delete 的動作,但你可以使用 Select 或 與其他資料表進行 join (ref:01)。
/*------------------------------------- create Procedure: pr_tb_insert -------------------------------------*/ CREATE PROCEDURE [dbo].[pr_tb_insert] -- Add the parameters for the stored procedure here @tb BKTable READONLY,@keyin nvarchar(50) AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; -- Insert statements for procedure here insert into TB select TName,@keyin from @tb END GO
以開發工具 VS2010 為例,先將自己的資料存到 Asp.Net 的 DataTable,後續要執行新增的動作時所要注意的地方,是SqlParameter 的資料型別需要設定為「SqlDbType.Structured」,而其他部份則跟你一般在寫的語法沒多大差別。執行完後,就可以看到因呼叫 Store Procedure :pr_tb_insert 而正確的將資料塞進資料表 TB 的結果。
DataTable dt = new DataTable(); DataColumn column = null; // add column1 column = new DataColumn(); column.DataType = System.Type.GetType("System.Int32"); column.ColumnName = "id"; dt.Columns.Add(column); // add column2 column = new DataColumn(); column.DataType = System.Type.GetType("System.String"); column.ColumnName = "Name"; dt.Columns.Add(column); DataRow dr = null; // add row1 dr = dt.NewRow(); dr["id"] = "1"; dr["Name"] = "paladin lee"; dt.Rows.Add(dr); // add row2 dr = dt.NewRow(); dr["id"] = "2"; dr["Name"] = "ken pen"; dt.Rows.Add(dr); // add row3 dr = dt.NewRow(); dr["id"] = "3"; dr["Name"] = "lillian cheng"; dt.Rows.Add(dr); Session["MyDT"] = dt; SqlConnection con; // 設定連線資訊 string conStr = System.Configuration.ConfigurationManager.ConnectionStrings["projmngConnectionString"].ConnectionString; con = new SqlConnection(conStr); con.Open(); using (con) { // Configure the SqlCommand and SqlParameter. SqlCommand sqlCmd = new SqlCommand("pr_tb_insert", con); sqlCmd.CommandType = CommandType.StoredProcedure; SqlParameter tvpParam = sqlCmd.Parameters.AddWithValue("@tb", dt); //設定 TVP tvpParam.SqlDbType = SqlDbType.Structured; //宣告傳入參數的型別是 TVP sqlCmd.Parameters.AddWithValue("@keyin", "jj"); sqlCmd.ExecuteNonQuery(); } con.Close();
測試的過程中,發現如果你習慣使用 SqlDataSource 來操作資料庫的動作,在處理 TVP 時,會因為找不到可以與參數型別 SqlDbType.Structured 相對應的設定而苦惱。這時可以透過 SessionParameter 指定 SessionField 來處理(ref 06)。它的原理,是將 DataTable 的資料存到 Session ,而後在 SqlDataSource 去指定那個 Session 來當參數。
程式參考:
.aspx
<asp:SqlDataSource ID="ds_insert" runat="server" ConnectionString="<%$ ConnectionStrings:projmngConnectionString %>" SelectCommand="pr_tb_insert" SelectCommandType="StoredProcedure"> <SelectParameters> <asp:SessionParameter ConvertEmptyStringToNull="False" Name="tb" SessionField="MyDT" /> <asp:Parameter ConvertEmptyStringToNull="False" Name="keyin" Type="String" /> </SelectParameters> </asp:SqlDataSource>
.aspx.cs
DataTable dt = new DataTable(); DataColumn column = null; // add column1 column = new DataColumn(); column.DataType = System.Type.GetType("System.Int32"); column.ColumnName = "id"; dt.Columns.Add(column); // add column2 column = new DataColumn(); column.DataType = System.Type.GetType("System.String"); column.ColumnName = "Name"; dt.Columns.Add(column); DataRow dr = null; // add row1 dr = dt.NewRow(); dr["id"] = "1"; dr["Name"] = "paladin lee"; dt.Rows.Add(dr); // add row2 dr = dt.NewRow(); dr["id"] = "2"; dr["Name"] = "ken pen"; dt.Rows.Add(dr); // add row3 dr = dt.NewRow(); dr["id"] = "3"; dr["Name"] = "lillian cheng"; dt.Rows.Add(dr); Session["MyDT"]=dt; ds_insert.SelectParameters["keyin"].DefaultValue = "jason"; ds_insert.Select(new DataSourceSelectArguments());
補充(20110408):
在一個專案上實際使用 TVP 的功能,而且也採用 SQLDataSource,因為我的資料表非常的大,沒想到發生了以下錯誤:
The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 1 ("@tb"): Data type 0x62 (sql_variant) has an invalid type for type-specific metadata.
目前對於這問題,還是沒有明確的解決方式,但放棄使用 SQLDataSource ,卻是眼下可行的方式。
ref 01:Table Valued Parameters New In SQL Server 2008 T-SQL Improvements
ref 02:SQL Server 2008: Table Valued Parameters
ref 03:Sql2008中使用DataTable作为存储过程的参数
ref 04:Table Value Parameters in SQL Server 2008 and .NET (C#)
ref 05:Sales Order Workshop - Articles at SQLServerCentral.com
ref 06:How to set up ASP.NET SQL Datasource to accept TVP
沒有留言:
張貼留言