将一组parameter passing给存储过程
我需要传递一个“id的”数组到一个存储过程,从表中删除所有行,除了与数组中的id匹配的行外。
我怎样才能以最简单的方式做到这一点?
使用存储过程:
编辑:序列化列表(或其他)的补充:
List<string> testList = new List<int>(); testList.Add(1); testList.Add(2); testList.Add(3); XmlSerializer xs = new XmlSerializer(typeof(List<int>)); MemoryStream ms = new MemoryStream(); xs.Serialize(ms, testList); string resultXML = UTF8Encoding.UTF8.GetString(ms.ToArray());
结果(准备使用XML参数):
<?xml version="1.0"?> <ArrayOfInt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <int>1</int> <int>2</int> <int>3</int> </ArrayOfInt>
原来的post:
传递XML作为参数:
<ids> <id>1</id> <id>2</id> </ids>
CREATE PROCEDURE [dbo].[DeleteAllData] ( @XMLDoc XML ) AS BEGIN DECLARE @handle INT EXEC sp_xml_preparedocument @handle OUTPUT, @XMLDoc DELETE FROM YOURTABLE WHERE YOUR_ID_COLUMN NOT IN ( SELECT * FROM OPENXML (@handle, '/ids/id') WITH (id INT '.') ) EXEC sp_xml_removedocument @handle
如果您使用的是Sql Server 2008或更高版本,则可以使用名为Table-Valued Parameter(TVP)的东西,而不是每次将其传递给存储过程时序列化和反序列化列表数据。
我们首先创build一个简单的模式来作为我们的游乐场:
CREATE DATABASE [TestbedDb] GO USE [TestbedDb] GO /* First, setup the sample program's account & credentials*/ CREATE LOGIN [testbedUser] WITH PASSWORD=N'µ×? ?S[°¿Q¥½q?_Ĭ¼Ð)3õļ%dv', DEFAULT_DATABASE=[master], DEFAULT_LANGUAGE=[us_english], CHECK_EXPIRATION=OFF, CHECK_POLICY=ON GO CREATE USER [testbedUser] FOR LOGIN [testbedUser] WITH DEFAULT_SCHEMA=[dbo] GO EXEC sp_addrolemember N'db_owner', N'testbedUser' GO /* Now setup the schema */ CREATE TABLE dbo.Table1 ( t1Id INT NOT NULL PRIMARY KEY ); GO INSERT INTO dbo.Table1 (t1Id) VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); GO
使用我们的模式和样本数据,我们现在准备创build我们的TVP存储过程:
CREATE TYPE T1Ids AS Table ( t1Id INT ); GO CREATE PROCEDURE dbo.FindMatchingRowsInTable1( @Table1Ids AS T1Ids READONLY ) AS BEGIN SET NOCOUNT ON; SELECT Table1.t1Id FROM dbo.Table1 AS Table1 JOIN @Table1Ids AS paramTable1Ids ON Table1.t1Id = paramTable1Ids.t1Id; END GO
使用我们的模式和API,我们可以从我们的程序中调用TVP存储过程,如下所示:
// Curry the TVP data DataTable t1Ids = new DataTable( ); t1Ids.Columns.Add( "t1Id", typeof( int ) ); int[] listOfIdsToFind = new[] {1, 5, 9}; foreach ( int id in listOfIdsToFind ) { t1Ids.Rows.Add( id ); } // Prepare the connection details SqlConnection testbedConnection = new SqlConnection( @"Data Source=.\SQLExpress;Initial Catalog=TestbedDb;Persist Security Info=True;User ID=testbedUser;Password=letmein12;Connect Timeout=5" ); try { testbedConnection.Open( ); // Prepare a call to the stored procedure SqlCommand findMatchingRowsInTable1 = new SqlCommand( "dbo.FindMatchingRowsInTable1", testbedConnection ); findMatchingRowsInTable1.CommandType = CommandType.StoredProcedure; // Curry up the TVP parameter SqlParameter sqlParameter = new SqlParameter( "Table1Ids", t1Ids ); findMatchingRowsInTable1.Parameters.Add( sqlParameter ); // Execute the stored procedure SqlDataReader sqlDataReader = findMatchingRowsInTable1.ExecuteReader( ); while ( sqlDataReader.Read( ) ) { Console.WriteLine( "Matching t1ID: {0}", sqlDataReader[ "t1Id" ] ); } } catch ( Exception e ) { Console.WriteLine( e.ToString( ) ); } /* Output: * Matching t1ID: 1 * Matching t1ID: 5 * Matching t1ID: 9 */
使用更抽象的API(比如Entity Framework)可能不会那么痛苦。 不过,我现在还没有时间亲眼看看。
这是最好的来源:
http://www.sommarskog.se/arrays-in-sql.html
使用链接创build一个拆分函数,并使用它:
DELETE YourTable FROM YourTable d LEFT OUTER JOIN dbo.splitFunction(@Parameter) s ON d.ID=s.Value WHERE s.Value IS NULL
我更喜欢数字表的方法
这是基于上述链接的代码,应该为你做…
在你使用我的函数之前,你需要设置一个“助手”表,每个数据库只需要这样做一次:
CREATE TABLE Numbers (Number int NOT NULL, CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] DECLARE @x int SET @x=0 WHILE @x<8000 BEGIN SET @x=@x+1 INSERT INTO Numbers VALUES (@x) END
使用这个函数来分割你的string,它不循环,速度非常快:
CREATE FUNCTION [dbo].[FN_ListToTable] ( @SplitOn char(1) --REQUIRED, the character to split the @List string on ,@List varchar(8000) --REQUIRED, the list to split apart ) RETURNS @ParsedList table ( ListValue varchar(500) ) AS BEGIN /** Takes the given @List string and splits it apart based on the given @SplitOn character. A table is returned, one row per split item, with a column name "ListValue". This function workes for fixed or variable lenght items. Empty and null items will not be included in the results set. Returns a table, one row per item in the list, with a column name "ListValue" EXAMPLE: ---------- SELECT * FROM dbo.FN_ListToTable(',','1,12,123,1234,54321,6,A,*,
,,,,B') returns: ListValue ———– 1 12 123 1234 54321 6 A *
B (10 row(s) affected) **/ —————- –SINGLE QUERY– –this will not return empty rows —————- INSERT INTO @ParsedList (ListValue) SELECT ListValue FROM (SELECT LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number – 1))) AS ListValue FROM ( SELECT @SplitOn + @List + @SplitOn AS List2 ) AS dt INNER JOIN Numbers n ON n.Number < LEN(dt.List2) WHERE SUBSTRING(List2, number, 1) = @SplitOn ) dt2 WHERE ListValue IS NOT NULL AND ListValue!='' RETURN END –Function FN_ListToTable
你可以使用这个函数作为连接中的一个表:
SELECT Col1, COl2, Col3... FROM YourTable INNER JOIN dbo.FN_ListToTable(',',@YourString) s ON YourTable.ID = s.ListValue
这里是你的删除:
DELETE YourTable FROM YourTable d LEFT OUTER JOIN dbo.FN_ListToTable(',',@Parameter) s ON d.ID=s.ListValue WHERE s.ListValue IS NULL
你可以试试这个:
DECLARE @List VARCHAR(MAX) SELECT @List = '1,2,3,4,5,6,7,8' EXEC( 'DELETE FROM TABLE WHERE ID NOT IN (' + @List + ')' )
您可以使用存储过程期望存在的临时表。 这将在旧版本的SQL Server上工作,不支持XML等
CREATE TABLE #temp (INT myid) GO CREATE PROC myproc AS BEGIN DELETE YourTable FROM YourTable LEFT OUTER JOIN #temp T ON T.myid=s.id WHERE s.id IS NULL END
declare @ids nvarchar(1000) set @ids = '100,2,3,4,5' --Parameter passed set @ids = ',' + @ids + ',' select * from TableName where charindex(',' + CAST(Id as nvarchar(50)) + ',', @ids) > 0
我会考虑将您的ID作为XMLstring传递,然后您可以将XML分解为临时表来join,或者也可以使用SP_XML_PREPAREDOCUMENT和OPENXML直接对XML进行查询。
怎么样使用XML数据types,而不是传递数组。 我发现这是一个更好的解决scheme,在SQL 2005中运行良好
我喜欢这个,因为它适合作为一个适用于SqlCommand的XElement传递
(对不起,这是VB.NET,但你明白了)
<Extension()> Public Function ToXml(Of T)(array As IEnumerable(Of T)) As XElement Return XElement.Parse( String.Format("<doc>{0}</doc>", String.Join("", array.Select(Function(s) String.Concat("<d>", s.ToString(), "</d>")))), LoadOptions.None) End Function
这是sql存储过程,缩短了,不完整!
CREATE PROCEDURE [dbo]。[myproc](@blah xml)
AS … WHERE SomeID IN(作为doc(t)从@ netwerkids.nodes(N'/ doc / d')selectdoc.t.value('。','int'))
在SQL Server 2016中,你可以用[]包装数组并将其作为JSON传递,参见http://blogs.msdn.com/b/sqlserverstorageengine/archive/2015/09/08/passing-arrays-to-t-sql-procedures -as-json.aspx