How can I declare and use T-SQL variables on multiple SqlCommands using the same SqlConnection object to make multiple insertions in a table variable?


I want to load a list of records given a possibly lengthy list of usernames (anywhere from one to thousands of usernames). Disregard how the name(s) are chosen, and assume they cannot be determined from any existing data in the database. This applies to SQL Server 2005.

I specifically want to avoid using a single select statement with thousands of expressions in the where clause, which would result in an excessively lengthy command text for the SqlCommand object (e.g. "...where n='bob000001' or n='bob000002' or ... or n='bob003000'"). Sounds reasonable?

I have decided to perform the select by populating a simple table variable with the usernames, then performing a select/join between the table variable and the table with the user data.

So, the first thing I need to do is populate the table variable. I have some problems here:

  • T-SQL syntax prior to SQL Server 2008 is verbose for inserting multiple rows into a table in a single statement, requiring something like multiple selects and union alls.
  • Rather than use verbose syntax of SS2005, or even the terse syntax available in SQL Server 2008, I am avoiding lengthy command texts altogether and just using multiple commands over a single connection.
  • Declaring a table variable in one SqlCommand, produces a "must declare the scalar variable" error when I try to use it in subsequent SqlCommands.
  • Involving stored procedures in any way may still involve passing huge strings, or may prevent variables from persisting outside the scope of the stored procedure. Assume creating stored procedures is not an option.

That third point is really the problem I'm trying to solve now. I've seen examples where people (claim to) successfully declare and use a variable in a single SqlCommand without an error. How can this be achieved when using multiple SqlCommand instances? I read that variables will persist for a single connection across multiple commands. Might involving a transaction help in some way?

Finally, keep in mind that I don't want to use temporary tables; doing so would offer a simple solution, but it also avoids the question I'm asking concerning variables and multiple SqlCommands; however, if you truly think that's the best option, feel free to say so.

Here is a code snippet that shows what's happening:

public static List<Student> Load( SqlConnection conn, List<StudentID> usernames )
    //Create table variable
    SqlCommand  command = new SqlCommand( "declare @s table (id varchar(30))", conn );

    //Populate a table variable with the usernames to load
    command = new SqlCommand( "insert into @s (id) values (@p)", conn );
    command.Parameters.Add( "@p", SqlDbType.VarChar );
    int len = usernames.Count;
    for (int i = 0; i < len; i++)
        command.Parameters["@p"].Value = usernames[i].ToString();
        command.ExecuteNonQuery(); //ERROR: must declare scalar variable @s

    //Select all students listed in the table variable
    command = new SqlCommand( "select StudentID, FName, LName, [etc.] from Student inner join @s on StudentID = order by StudentID", conn );

    //Execute the query to get the student info from the database.
    List<Student> students = new List<Student>()
    using(SqlDataReader reader = command.ExecuteReader())
        //code to load results and populate students list
    return students;

Note: I'm aware that an SqlCommand involving parameters internally calls a stored procedure, which would normally prevent persisting variables across multiple SqlCommands, but the first query that declares the table variable doesn't involve parameters (i.e. no reference to command.Parameters.AddWithValue is made), so it should be in scope for later commands that may call stored procedures.

Edit: To use a temp table, one just has to change "@s" to "#s" and "declare @s table" to "create table #s", which is nice. One may also want to drop the temp table at the end, but it's not necessary.

Use a temporary table which persists for the duration of the session/connection (multiple calls). Table variables have scope only for the batch, which is basically one call.