1

I have a table DEPT, which holds 2 columns - ID, NAME.

A search form is presented with the IDs from the DEPT table and the user can chose any number of IDs and submit the form, to get the related NAMEs.

Clarification/Inputs:

  • I don't want to build a dynamic query - its not manageable.
  • I prefer a stored procedure using table-valued parameters

Any other solutions to proceed?

NOTE:
This example is simple with 1 table - in real life, I have to deal with more than 6 tables!

Thanks for any suggestions

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
hyacinth
  • 19
  • 2
  • What do you mean by a dynamic query not being manageable? It would be easier to say `IN (1,2,4,5)` then to build a table-valued parameter. – mellamokb May 24 '12 at 19:48
  • Can you use XML parameters? (version of SQL not specified) – StingyJack May 24 '12 at 19:51
  • 2
    possible duplicate of [Passing an array of parameters to Stored Procedure](http://stackoverflow.com/questions/1069311/passing-an-array-of-parameters-to-stored-procedure) – StingyJack May 24 '12 at 19:52
  • 2
    What is the relevance of the `EXISTS` in your title? – Dan Puzey May 24 '12 at 19:57
  • I don't like the proposed duplicate - the accepted answer uses an XML document method, and none of the answers use table-valued parameters. [How about this one instead?](http://stackoverflow.com/a/10405622/61305) He'd only have to change it from an INSERT/SELECT to a SELECT ... WHERE IN. – Aaron Bertrand May 24 '12 at 20:14
  • Why would you want to use stored procs any more? – Basic Jun 14 '12 at 23:33
  • @Basic Because we don't all want to recompile our applications just to refactor or optimize SQL queries. Putting all the SQL in the EF code, why not just use Access? You're taking away many of the benefits of using SQL Server in the first place. Yeah, you deploy your app a week sooner but you troubleshoot it for months longer. – Aaron Bertrand Jun 14 '12 at 23:43
  • @AaronBertrand: I replied on your post before reading this. Thanks for the POV – Basic Jun 14 '12 at 23:52

1 Answers1

8
CREATE TYPE dbo.DeptList
AS TABLE
(
  ID INT
);
GO

CREATE PROCEDURE dbo.RetrieveDepartments
  @dept_list AS dbo.DeptList READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT Name FROM dbo.table1 WHERE ID IN (SELECT ID FROM @dept)
  UNION ALL 
  SELECT Name FROM dbo.table2 WHERE ID IN (SELECT ID FROM @dept)
  -- ...
END
GO

Now in your C# code, create a DataTable, fill it in with the IDs, and pass it in to the stored procedure. Assuming you already have a list called tempList and the IDs are stored in id:

DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("ID"));

foreach(var item in tempList)
{ 
    tvp.Rows.Add(item.id); 
}

using (connObject)
{
    SqlCommand cmd = new SqlCommand("StoredProcedure", connObject);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter tvparam = cmd.Parameters.AddWithValue("@dept_list", tvp);
    tvparam.SqlDbType = SqlDbType.Structured;
    ...
}

You can also use a split function. Many exist, this is the one I like if you can guarantee that the input is safe (no <, >, & etc.):

CREATE FUNCTION dbo.SplitInts_XML
(
   @List       VARCHAR(MAX),
   @Delimiter  CHAR(1)
)
RETURNS TABLE
AS
   RETURN 
   (  
      SELECT Item = 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 procedure can be:

CREATE PROCEDURE dbo.RetrieveDepartments
  @dept_list VARCHAR(MAX)
AS
BEGIN
  SET NOCOUNT ON;

  ;WITH d AS (SELECT ID = Item FROM dbo.SplitInts(@dept_list, ','))
  SELECT Name FROM dbo.table1 WHERE ID IN (SELECT ID FROM d)
  UNION ALL
  SELECT Name FROM dbo.table2 WHERE ID IN (SELECT ID FROM d)
  -- ...
END
GO
jcolebrand
  • 15,889
  • 12
  • 75
  • 121
Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
  • I know this is what the OP asked (so +1) for but what an unholy mess - Doing it EF would take a 4-line class and a lambda. I can't help thinking this is doing it the hard way for the sake of it. – Basic Jun 14 '12 at 23:32
  • Hard to some people is simple for others. It's like a lot of things - what works best for you is not necessarily what everyone wants. – Aaron Bertrand Jun 14 '12 at 23:44
  • I understand that - otherwise I wouldn't have +1'd - It's what the OP asked for so it's how you answered, but I'm curious why anyone would opt for this method if if not forced to? I don't see any security benefit (SPs aren't more secure than parameterised Qs), There's possible benefit in performance as the execution plan is re-used but that can also be done more readably/with less code using compiled queries, etc... Is this how you'd do it by choice? If so, do you mind telling me why? Perhaps I'm just missing something – Basic Jun 14 '12 at 23:51
  • Because I optimize at the database level, and want to be able to do that even after the app is deployed, without having to recompile and redeploy the app. It's also nice if multiple apps share functionality they can call the same stored procedure, instead of each having to have their own copy of the same SQL query (and the middle tier, and validation, etc. etc.). It's pretty simple really and (for me, at least) has nothing to do with security or plan re-use (though the latter can be a serious issue with early versions of Linq to SQL). One other reason: there are a lot of things that you can do – Aaron Bertrand Jun 14 '12 at 23:54
  • in a stored procedure that EF and other ORM stuff just doesn't translate well. Someone the other day was trying to convert a right join, and some EF expert said "you can't do that; rewrite it as a left join." I don't know if that's true or not, but I have seen *many* occasions on StackOverflow where EF / Linq etc. produced significant roadblocks to common problems that are trivial to solve in T-SQL but that these "helpful" relationship builders don't understand. – Aaron Bertrand Jun 14 '12 at 23:55
  • If you can solve this with 4 lines in EF, why not post that as an answer? The challenge is, does the user even know what EF is? Will he be able to completely re-write his architecture to use EF just so he doesn't have to use these 20 lines he's been handed on a silver platter? With a stored procedure, you can access the methodology pretty much from any data access technology from any language - with minimal change to the app. Heck you can write an ASP page in three minutes that takes advantage of this stored procedure. I wonder how long it would take to convert the app to use EF... – Aaron Bertrand Jun 14 '12 at 23:58
  • Man, I need to get a life. I know I'm never going to convince an EF fan why stored procedures are better. It's like trying to convince a vegan that bacon doesn't suck (even though they know, deep down, I'm right). – Aaron Bertrand Jun 14 '12 at 23:59
  • Wow, before we start getting personal and calling people fans, I spent 4 years at my last place using stored procs. Admittedly, it was fairly old stuff that was hobbling along (and yes, some of them even built up SQL strings gah!) so it may not be the best comparison. I was attempting to have a sensible discussion. Clearly you are not. I've used both and while there are some benefits to SPs, for me at least, they're negligible compared to the convenience of EF. If you feel like having a sensible, balanced discussion about the topic, not the person, let me know – Basic Jun 15 '12 at 00:03
  • I said "fan" you read "fanboi" - wasn't my intent, sorry. I'm a fan of stored procedures, and I won't get offended if you call me that. – Aaron Bertrand Jun 15 '12 at 00:04
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/12581/discussion-between-basic-and-aaron-bertrand) – Basic Jun 15 '12 at 00:06