I am just starting to use table valued parameter in sql server 2008 to update table. basically, I create user defined table type mapping to an existing table column by column, and use that as table valued parameter, passing data to the server for delete, update and insert. in most case, that works fine. however, for table with composite primary keys, when part of the keys got changed, this approach would fail, as the tvp only contains one set of the key values (either current or original). My question is what is the best way to handle such cases? do I have to add a second sets of primary keys columns in the table type definition?
A simplified example: Table:
CREATE TABLE [dbo].[Prices](
[ID] [int] NOT NULL PRIMARY KEY,
[Date] [smalldatetime] NOT NULL PRIMARY KEY,
[Value] [float] NOT NULL,
CONSTRAINT [PK_Prices] PRIMARY KEY CLUSTERED
(
[ID] ASC,
[Date] ASC
)
)
Table Type
CREATE TYPE [dbo].[tvp_Prices] AS TABLE(
[ID] [int] NOT NULL,
[Date] [smalldatetime] NOT NULL,
[Value] [float] NOT NULL
)
Add some data to the table
Insert Into [dbo].[Prices] Values (1, '1/1/2015', 1.2)
Insert Into [dbo].[Prices] Values (1, '1/2/2015', 1.3)
Insert Into [dbo].[Prices] Values (2, '1/1/2015', 1.4)
Insert Into [dbo].[Prices] Values (2, '1/2/2015', 1.5)
in c#, load the Prices to DataTable pricesTable,
update some value:
pricesTable.Rows[1]["Date"] = new DateTime(2015, 1, 3);
Now the question how to use the table valued parameter (tvp_Prices) to save the changes back to the server?
Table value parameters is used to pass multiple rows of data through parameters to, for example, stored procedure. In your case the use case could be the next:
CREATE TYPE [dbo].[tvp_Prices] AS TABLE(
[ID] [int] NOT NULL,
[Date] [smalldatetime] NOT NULL,
[Value] [float] NOT NULL
);
CREATE TABLE [dbo].[Prices](
[ID] [int] NOT NULL PRIMARY KEY,
[Date] [smalldatetime] NOT NULL PRIMARY KEY,
[Value] [float] NOT NULL,
CONSTRAINT [PK_Prices] PRIMARY KEY CLUSTERED
(
[ID] ASC,
[Date] ASC
)
);
CREATE PROC dbo.use_Prices
(
@prices [dbo].[tvp_Prices] READONLY
)
AS BEGIN
INSERT INTO [dbo].[Prices] ([ID], [Date], [Value])
SELECT * FROM @prices;
END
-- usage ---------------------------------
DECLARE
@prices [dbo].[tvp_Prices];
Insert Into @prices Values (1, '1/1/2015', 1.2)
Insert Into @prices Values (1, '1/2/2015', 1.3)
Insert Into @prices Values (2, '1/1/2015', 1.4)
Insert Into @prices Values (2, '1/2/2015', 1.5)
SELECT * FROM [dbo].[Prices]
EXEC dbo.use_Prices @prices;
SELECT * FROM [dbo].[Prices]
Result
ID Date Value
-----------------------------
-
ID Date Value
-----------------------------
1 2015-01-01 00:00:00 1.2
1 2015-01-02 00:00:00 1.3
2 2015-01-01 00:00:00 1.4
2 2015-01-02 00:00:00 1.5
CREATE PROC dbo.use_UpdatePrices
(
@prices [dbo].[tvp_Prices] READONLY
)
AS BEGIN
-- update values by the PK
UPDATE p
SET
p.[Value] = p1.[Value]
FROM [dbo].[Prices] p
JOIN @prices p1 ON p1.ID = p.[ID]
AND p1.[Date] = p.[Date];
END
Truncate table [dbo].[Prices]
Insert Into [dbo].[Prices] Values (1, '1/1/2015', 1.2)
Insert Into [dbo].[Prices] Values (1, '1/2/2015', 1.3)
Insert Into [dbo].[Prices] Values (2, '1/1/2015', 1.4)
Insert Into [dbo].[Prices] Values (2, '1/2/2015', 1.5)
-- usage -----------------------------------
DECLARE
@prices [dbo].[tvp_Prices];
-- values to update
Insert Into @prices Values (1, '1/1/2015', 10.10)
Insert Into @prices Values (1, '1/2/2015', 11.11)
SELECT * FROM [dbo].[Prices]
EXEC dbo.use_UpdatePrices @prices;
SELECT * FROM [dbo].[Prices]
Output
ID Date Value
-----------------------------
1 2015-01-01 00:00:00 1.2
1 2015-01-02 00:00:00 1.3
2 2015-01-01 00:00:00 1.4
2 2015-01-02 00:00:00 1.5
ID Date Value
-----------------------------
1 2015-01-01 00:00:00 10.1
1 2015-01-02 00:00:00 11.11
2 2015-01-01 00:00:00 1.4
2 2015-01-02 00:00:00 1.5