![]() |
| 圖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



沒有留言:
張貼留言