问题
I have a table,
FruitCrateID
FruitID
Now FruitID
and FruitCrateID
have a many-to-many relationship. I have a strongly typed dataset I need to create add and update stored procedure.
Now I can use foreach
to add FruitID
and FruitCrateID
to database but there will be several calls for example,
FruitCrateID - 124
FruitID - 23
FruitID - 245
FruitID - 242
and so on ....
What I created so far.. I want to add them all in a single query..
IF (EXISTS (SELECT * FROM FruitShoppingList WHERE FruitCrateID = @FruitCrateID))
BEGIN
DELETE FROM FruitShoppingList WHERE FruitCrateID = @FruitCrateID
END
INSERT INTO dbo.FruitShoppingList (FruitID, FruitCrateID)
VALUES ( @FruitID, @FruitCrateID)
回答1:
I believe you're looking for a table valued parameter You can populate it from a dataset and, assuming you don't do something silly when you pass it to SQL Server, will be treated as an atomic operation - all of it's going in or none of it.
IF EXISTS
(
SELECT
*
FROM
sys.types T
WHERE
T.name = 'CONTRIVED_EXAMPLE'
)
BEGIN
PRINT 'Dropping type dbo.CONTRIVED_EXAMPLE'
DROP TYPE dbo.CONTRIVED_EXAMPLE
END
PRINT 'Creating type dbo.CONTRIVED_EXAMPLE'
GO
-----------------------------------------------------------------------------
-- Type: dbo.CONTRIVED_EXAMPLE
-- Author: Bill Fellows
-- Date: 2010-06-07
--
-- This is a user-defined table type demonstrating the syntax
-- for a table valued parameter
--
-- See also:
--
-- Modified:
--
-----------------------------------------------------------------------------
CREATE TYPE
dbo.CONTRIVED_EXAMPLE AS TABLE
(
twitter_handle varchar(15) NOT NULL
, have_met bit NOT NULL
)
GO
IF EXISTS
(
SELECT
*
FROM
sys.tables T
WHERE
T.name = 'CONTRIVED_EXAMPLE_TABLE'
)
BEGIN
PRINT 'Dropping type dbo.CONTRIVED_EXAMPLE_TABLE'
DROP TABLE dbo.CONTRIVED_EXAMPLE_TABLE
END
PRINT 'Creating table dbo.CONTRIVED_EXAMPLE_TABLE'
GO
-----------------------------------------------------------------------------
-- Type: dbo.CONTRIVED_EXAMPLE_TABLE
-- Author: Bill Fellows
-- Date: 2010-06-07
--
-- This is a boring table demonstrating the syntax
-- for a table valued parameter
--
-- See also:
--
-- Modified:
--
-----------------------------------------------------------------------------
CREATE TABLE
dbo.CONTRIVED_EXAMPLE_TABLE
(
row_id int identity(1,10) NOT NULL PRIMARY KEY
, twitter_handle varchar(15) NOT NULL
, have_met bit NOT NULL
)
GO
IF EXISTS
(
SELECT
SO.*
FROM
dbo.sysobjects SO
WHERE
SO.id = OBJECT_ID('dbo.EchoBack')
AND OBJECTPROPERTY(SO.id, 'IsProcedure') = 1
)
BEGIN
PRINT 'Dropping stored procedure dbo.EchoBack'
DROP PROCEDURE dbo.EchoBack
END
GO
PRINT 'Creating stored procedure dbo.EchoBack'
GO
-----------------------------------------------------------------------------
-- Function: dbo.EchoBack
-- Author: Bill Fellows
-- Date: 2010-06-07
--
-- This procedure handles merging
--
-- Recordsets:
-- The contents of our table valued parameter will be echoed back to the
-- caller.
--
-- Side-effects:
-- None
--
-- See also:
-- your mom
--
-- Modified:
--
-----------------------------------------------------------------------------
CREATE PROCEDURE dbo.EchoBack
(
@tvp dbo.CONTRIVED_EXAMPLE READONLY
)
AS
BEGIN
SET NOCOUNT ON
SELECT
T.*
FROM
@tvp T
END
GO
PRINT 'Granting rights for dbo.EchoBack'
GRANT EXECUTE ON dbo.EchoBack TO public
GO
DECLARE @p1 dbo.CONTRIVED_EXAMPLE
INSERT INTO @p1 values('billinkc', 1)
INSERT INTO @p1 values('peschkaj', 1)
INSERT INTO @p1 values('mikeSQL', 1)
INSERT INTO @p1 values('SQLChicken', 0)
SELECT * FROM @p1
EXECUTE dbo.EchoBack @p1
GO
IF EXISTS
(
SELECT
SO.*
FROM
dbo.sysobjects SO
WHERE
SO.id = OBJECT_ID('dbo.TwitterAdd')
AND OBJECTPROPERTY(SO.id, 'IsProcedure') = 1
)
BEGIN
PRINT 'Dropping stored procedure dbo.TwitterAdd'
DROP PROCEDURE dbo.TwitterAdd
END
GO
PRINT 'Creating stored procedure dbo.TwitterAdd'
GO
-----------------------------------------------------------------------------
-- Function: dbo.TwitterAdd
-- Author: Bill Fellows
-- Date: 2010-06-07
--
-- This procedure handles merging
--
-- Recordsets:
-- Business rule violations (optional)
--
-- Side-effects:
-- 0-N rows written to twitter table
--
-- See also:
-- CONTRIVED_EXAMPLE (your mom)
--
-- Modified:
--
-----------------------------------------------------------------------------
CREATE PROCEDURE dbo.TwitterAdd
(
@tvp dbo.CONTRIVED_EXAMPLE READONLY
)
AS
BEGIN
SET NOCOUNT ON
MERGE
dbo.CONTRIVED_EXAMPLE_TABLE DESTINATION
USING
(SELECT twitter_handle, have_met FROM @tvp T) AS SOURCE (twitter_handle, have_met)
ON (SOURCE.twitter_handle = DESTINATION.twitter_handle)
-- Never forget I met someone
WHEN MATCHED AND DESTINATION.have_met = 0
THEN UPDATE SET DESTINATION.have_met = SOURCE.have_met
WHEN NOT MATCHED
THEN INSERT(twitter_handle, have_met) VALUES (SOURCE.twitter_handle, SOURCE.have_met)
-- merge statements must be terminated by a semi-colon
-- and don't you forget it!
;
END
GO
PRINT 'Granting rights for dbo.TwitterAdd'
GRANT EXECUTE ON dbo.TwitterAdd TO public
GO
At this point, you can see how the TVP works. The only remaining step is to tie it in to your .NET code. Something like
/// <summary>
/// This method demonstrates the ease of passing a dataset in
/// as a TVP
/// </summary>
public static void TSQLTuesday007()
{
string connectionString = @"Data Source=localhost;Initial Catalog=master;Integrated Security=True";
System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(connectionString);
System.Data.DataTable dataTable = null;
// note that my data table does not have to be the same
// as my UDTT, nor do my columns have to have the same name
// Types-yes. Ordinal, probably so
dataTable = new System.Data.DataTable("Sample");
dataTable.Columns.Add("tweep", System.Type.GetType("System.String"));
dataTable.Columns.Add("have_met", System.Type.GetType("System.Boolean"));
// add rows to my data table but really, this could be any source
dataTable.Rows.Add(new object[] { "billinkc", true });
dataTable.Rows.Add(new object[] { "BrentO", true });
dataTable.Rows.Add(new object[] { "buckwoody", false });
// Hooray for #sqlsat35 and meeting Jen & Sean
dataTable.Rows.Add(new object[] { "MidnightDBA", true });
System.Data.SqlClient.SqlConnection connection = null;
System.Data.DataSet results = null;
System.Data.SqlClient.SqlCommand command = null;
System.Data.SqlClient.SqlDataReader dataReader = null;
connection = new System.Data.SqlClient.SqlConnection(connectionString);
try
{
connection.Open();
command = new System.Data.SqlClient.SqlCommand("TwitterAdd");
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Connection = connection;
// Assigning a table valued parameter looks much like any other parameter
System.Data.SqlClient.SqlParameter tvp = command.Parameters.AddWithValue("@tvp", dataTable);
// this is the only special sauce (not required but helpful)
tvp.SqlDbType = System.Data.SqlDbType.Structured;
tvp.TypeName = "dbo.CONTRIVED_EXAMPLE";
dataReader = command.ExecuteReader();
if (dataReader.HasRows)
{
results = new System.Data.DataSet();
results.Tables.Add();
results.Tables[0].Load(dataReader);
// PPrint(results);
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
来源:https://stackoverflow.com/questions/25869110/stored-procedure-insert-multiple-rows