Ich muss ein Array von "IDs" an eine gespeicherte Prozedur übergeben, um alle Zeilen aus der Tabelle zu löschen, AUSSER die Zeilen, die mit den IDs im Array übereinstimmen.
Wie kann ich das auf einfachste Weise tun?
Ich muss ein Array von "IDs" an eine gespeicherte Prozedur übergeben, um alle Zeilen aus der Tabelle zu löschen, AUSSER die Zeilen, die mit den IDs im Array übereinstimmen.
Wie kann ich das auf einfachste Weise tun?
Antworten:
Verwenden Sie eine gespeicherte Prozedur:
BEARBEITEN: Eine Ergänzung zum Serialisieren der Liste (oder irgendetwas anderem):
Das Ergebnis (bereit zur Verwendung mit XML-Parametern):
ORIGINAL POST:
XML als Parameter übergeben:
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
quelle
Wenn Sie SQL Server 2008 oder besser verwenden, können Sie einen so genannten Table-Valued Parameter (TVP) verwenden, anstatt Ihre Listendaten jedes Mal zu serialisieren und zu deserialisieren, wenn Sie sie an eine gespeicherte Prozedur übergeben möchten.
Beginnen wir mit der Erstellung eines einfachen Schemas als Spielplatz:
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
Mit unserem Schema und den Beispieldaten können wir jetzt unsere gespeicherte TVP-Prozedur erstellen:
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
Wenn sowohl unser Schema als auch unsere API vorhanden sind, können wir die gespeicherte TVP-Prozedur aus unserem Programm wie folgt aufrufen:
// 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 */
Es gibt wahrscheinlich einen weniger schmerzhaften Weg, dies mit einer abstrakteren API wie Entity Framework zu tun. Derzeit habe ich jedoch keine Zeit, mich selbst davon zu überzeugen.
quelle
Dies ist die beste Quelle:
http://www.sommarskog.se/arrays-in-sql.html
Erstellen Sie eine Split-Funktion über den Link und verwenden Sie sie wie folgt:
DELETE YourTable FROM YourTable d LEFT OUTER JOIN dbo.splitFunction(@Parameter) s ON d.ID=s.Value WHERE s.Value IS NULL
Ich bevorzuge den Nummerntabellenansatz
Dies ist Code basierend auf dem obigen Link, der es für Sie tun sollte ...
Bevor Sie meine Funktion verwenden, müssen Sie eine "Hilfstabelle" einrichten. Sie müssen dies nur einmal pro Datenbank tun:
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
Verwenden Sie diese Funktion, um Ihren String zu teilen, der keine Schleife durchläuft und sehr schnell ist:
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
Sie können diese Funktion als Tabelle in einem Join verwenden:
SELECT Col1, COl2, Col3... FROM YourTable INNER JOIN dbo.FN_ListToTable(',',@YourString) s ON YourTable.ID = s.ListValue
Hier ist dein Löschen:
DELETE YourTable FROM YourTable d LEFT OUTER JOIN dbo.FN_ListToTable(',',@Parameter) s ON d.ID=s.ListValue WHERE s.ListValue IS NULL
quelle
Sie könnten dies versuchen:
DECLARE @List VARCHAR(MAX) SELECT @List = '1,2,3,4,5,6,7,8' EXEC( 'DELETE FROM TABLE WHERE ID NOT IN (' + @List + ')' )
quelle
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
quelle
Sie können eine temporäre Tabelle verwenden, deren Existenz die gespeicherte Prozedur erwartet. Dies funktioniert mit älteren Versionen von SQL Server, die XML usw. nicht unterstützen.
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
quelle
Ich würde in Betracht ziehen, Ihre IDs als XML-Zeichenfolge zu übergeben, und dann könnten Sie das XML in eine temporäre Tabelle zerlegen, um es zu verknüpfen , oder Sie könnten das XML auch direkt mit SP_XML_PREPAREDOCUMENT und OPENXML abfragen .
quelle
Was ist mit der Verwendung des XML-Datentyps anstelle der Übergabe eines Arrays? Ich finde das eine bessere Lösung und funktioniert gut in SQL 2005
quelle
Ich mag dieses, weil es geeignet ist, als XElement übergeben zu werden, das für SqlCommand geeignet ist
(Sorry, es ist VB.NET, aber Sie haben die Idee)
<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
Dies ist der SQL Stored Proc, verkürzt, nicht vollständig!
CREATE PROCEDURE [dbo]. [Myproc] (@blah xml)
AS ... WHERE SomeID IN (SELECT doc.t.value ('.', 'Int') von @ netwerkids.nodes (N '/ doc / d') ) als doc (t))
quelle
In SQL Server 2016 können Sie das Array mit [] umschließen und als JSON übergeben (siehe http://blogs.msdn.com/b/sqlserverstorageengine/archive/2015/09/08/passing-arrays-to-t-sql-procedures) -as-json.aspx
quelle
Sie können die Funktion STRING_SPLIT in SQL Server verwenden. Sie können die Dokumentation hier überprüfen .
DECLARE @YourListOfIds VARCHAR(1000) -- Or VARCHAR(MAX) depending on what you need SET @YourListOfIds = '1,2,3,4,5,6,7,8' SELECT * FROM YourTable WHERE Id IN(SELECT CAST(Value AS INT) FROM STRING_SPLIT(@YourListOfIds, ','))
quelle