Should table-valued parameters be used here?

前端 未结 1 389
闹比i
闹比i 2021-01-23 16:00

I have following query:

 @\"UPDATE students SET IsDeleted = 1 WHERE StudentId IN (
         SELECT StudentId FROM Class where PassId IN (
              SELECT Id         


        
相关标签:
1条回答
  • 2021-01-23 16:44

    Yes, use table-valued parameters. First create a type:

    CREATE TYPE dbo.StudentIDs(ID INT PRIMARY KEY);
    

    Now your procedure can use this (note that I've changed your nested IN queries to a proper join):

    CREATE PROCEDURE dbo.MarkStudentsAsDeleted
      @IDs dbo.StudentIDs READONLY
    AS
    BEGIN
      SET NOCOUNT ON;
    
      UPDATE s SET IsDeleted = 1
        FROM dbo.Students AS s
        INNER JOIN dbo.Class AS c
        ON s.StudentId = c.StudentId
        INNER JOIN dbo.ClassValueTable AS ct
        ON c.PassId = ct.Id
        WHERE ct.IsDeleted <> 1
        AND EXISTS (SELECT 1 FROM @IDs WHERE StudentID = s.StudentID);
    END 
    GO
    

    And your C# code would pass in a DataTable or however you've assembled the collection of activeIds, rather than a comma-separated list.

    DataTable dt = new DataTable();
    dt.Columns.Add("ID", typeof(int));
    dt.Rows.Add(1);
    dt.Rows.Add(2);
    dt.Rows.Add(3);
    dt.Rows.Add(4);
    
    ... open connection etc. ...
    
    SqlCommand cmd = new SqlCommand("dbo.MarkStudentsAsDeleted", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter tvp1 = cmd.Parameters.AddWithValue("@IDs", dt);
    tvp1.SqlDbType = SqlDbType.Structured;
    cmd.ExecuteNonQuery();
    
    ... close, dispose, etc. ...
    

    If you want to insist on passing a string into the stored procedure, you'll need to use a split function. For example:

    CREATE FUNCTION dbo.SplitInts
    (
       @List       VARCHAR(MAX),
       @Delimiter  VARCHAR(255) = ','
    )
    RETURNS TABLE
    WITH SCHEMABINDING 
    AS
      RETURN 
      (  
        SELECT [value] = y.i.value('(./text())[1]', 'int')
        FROM 
        ( 
          SELECT x = CONVERT(XML, '<i>' 
            + REPLACE(@List, @Delimiter, '</i><i>') 
            + '</i>').query('.')
        ) AS a CROSS APPLY x.nodes('i') AS y(i)
      );
    GO
    

    Now your stored procedure can be:

    CREATE PROCEDURE dbo.MarkStudentsAsDeleted
      @IDs VARCHAR(MAX)
    AS
    BEGIN
      SET NOCOUNT ON;
    
      UPDATE s SET IsDeleted = 1
        FROM dbo.Students AS s
        INNER JOIN dbo.Class AS c
        ON s.StudentId = c.StudentId
        INNER JOIN dbo.ClassValueTable AS ct
        ON c.PassId = ct.Id
        WHERE ct.IsDeleted <> 1
        AND EXISTS (SELECT 1 FROM dbo.SplitInts(@IDs, ',') WHERE Item = s.StudentID);
    END 
    GO
    
    0 讨论(0)
提交回复
热议问题