Improving performance of sql query

Christina

I have a query that I am writing in my C# code to retrieve results from database. Based on the user input, my query will change.
I have a user input say 'challenge'. And 'challenge' can have three values 0,1 and 2, based on what user selects in UI.
If user selects challenge 0, the query will be different.
If user selects challenge 1, the query will be different.
And if user selects challenge 2, the query will be union of query for challenge 0 and query for challenge 1.

Below is how my code for generating sql query looks at present:

         string sql = @"SELECT
            tbl.given_id, name, message, tbl.emplid, category, created_date
        FROM
            dbo.myTable tbl

        WHERE
           created_date >= @dtFrom
            AND created_date < @dtTo
        ";


    //Include only challenge 0  
         if(challenge == 0)
                        {
                            sql += " AND emplid IN (" + sb.ToString() + ")";  //sb.ToString() is list of emplids selected from UI.
                        }

    //Include only challenge 1    
         if (challenge == 1)
                        {
                            sql += " AND given_Id IN (SELECT DISTINCT rec.given_Id  from dbo.Recipient rec WHERE rec.given_Id = tbl.given_Id AND rec.emplid IN (" + sb.ToString() + ")) ";
                        }  


//Include both challenge 0 and 1

    if (challenge == 2)
                    {
                        sql = String.Format(@"
    SELECT * FROM
    (
        (
            SELECT
                tbl.given_id, name, message, tbl.emplid, category, created_date
            FROM
                dbo.myTable tbl             
            WHERE
                created_date >= @dtFrom
                AND created_date < @dtTo
                AND tbl.emplid IN ({0})
        )
        UNION
        (
           SELECT
                tbl.given_id, name, message, tbl.emplid, category, created_date
            FROM
                dbo.myTable tbl             
            WHERE
                created_date >= @dtFrom
                AND created_date < @dtTo
                AND given_Id IN (SELECT DISTINCT rec.given_Id  from dbo.Recipient rec WHERE rec.given_Id = tbl.given_Id AND rec.emplid IN (" + sb.ToString() + "))

        )

    )
    ", sb.ToString());

                    }   

So, if(challenge == 2), is generating the query by the union of query generated in challenge = 0 and challenge = 1.
The query code in challenge = 2, is just repetitive of both the above queries.
Is there any other way of writing such query, so that I can improve my query and increase the perfomance?

Thanks.

Edit - Writing just the queries generated to simplify my question:

Query 1 -

SELECT
                tbl.given_id, name, message, tbl.emplid, category, created_date
            FROM
                dbo.myTable tbl             
            WHERE
                created_date >= @dtFrom
                AND created_date < @dtTo
                AND tbl.emplid IN (@lst_emplids)  

Query 2

SELECT
                    tbl.given_id, name, message, tbl.emplid, category, created_date
                FROM
                    dbo.myTable tbl             
                WHERE
                    created_date >= @dtFrom
                    AND created_date < @dtTo
                    AND given_Id IN (SELECT DISTINCT rec.given_Id  from dbo.Recipient rec WHERE rec.given_Id = tbl.given_Id AND rec.emplid IN (@lst_emplids)  

Query 3

SELECT * FROM
        (
            (
                SELECT
                    tbl.given_id, name, message, tbl.emplid, category, created_date
                FROM
                    dbo.myTable tbl             
                WHERE
                    created_date >= @dtFrom
                    AND created_date < @dtTo
                    AND tbl.emplid IN (@lst_emplids)
            )
            UNION
            (
               SELECT
                    tbl.given_id, name, message, tbl.emplid, category, created_date
                FROM
                    dbo.myTable tbl             
                WHERE
                    created_date >= @dtFrom
                    AND created_date < @dtTo
                    AND given_Id IN (SELECT DISTINCT rec.given_Id  from dbo.Recipient rec WHERE rec.given_Id = tbl.given_Id AND rec.emplid IN (@lst_emplids)  

            )

        )
Clay

There are a number of things you can do. First, it would be worthwhile to have SQL do the heavy lifting. You can pass the parameters to it and let it figure out how to arrange the query based on the arguments.

To take best advantage, you'd wrap your SQL up in a pre-defined procedure or function. In this case, I think I'd go with a function - but either is fine. My example is a function.

In either case, you can have SQL treat your list of employeeIDs as a table. To have that support, you'd create a table type in SQL:

create type dbo.IntIdsType as table ( Id int )

Table types are just declarations of structure - they're not table's per se. You can subsequently declare variables of that type.

Once you have a table type, your function would take all the arguments and figure out what to do:

Edit 2: I provided some sketchy conditional logic earlier - this has been modified in the function below:

create function dbo.GetMyTable
( 
    @from datetime, 
    @to datetime, 
    @challenge int, 
    @employeeIds dbo.IntIdsType readonly 
)
returns table as return
    select
        tbl.given_id, name, message, tbl.emplid, category, created_date
    from
        dbo.myTable tbl             
    where
        created_date >= @dtFrom 
        and
        created_date < @dtTo 
        and
        (
            (
                @challenge = 0 
                and 
                tbl.emplid in ( select Id from @employeeIds )
            )
            or
            (
                @challenge = 1
                and
                given_Id in 
                (
                    select 
                        rec.given_Id  
                    from 
                        dbo.Recipient rec 
                    where 
                        rec.given_Id = tbl.given_Id 
                        and 
                        rec.emplid in ( select Id from @employeeIds )
                )
            )
            or
            (
                @challenge = 2
                and
                (
                    (
                        @challenge = 0 
                        and 
                        tbl.emplid in ( select Id from @employeeIds )
                    )
                    or
                    (
                        given_Id in 
                        (
                            select 
                                rec.given_Id  
                            from 
                                dbo.Recipient rec 
                            where 
                                rec.given_Id = tbl.given_Id 
                                and 
                                rec.emplid in ( select Id from @employeeIds )
                        )
                    )
                )
            )
        )

Note the lack of a union - and how this lets SQL decide to use a predicate based on the value of the arguments. Also - notice how the passed in table-valued parameter is used just like it was a table.

Setting up the call is a little noisy - maybe something like:

async Task GetMyData( DateTime fromDate, DateTime toDate, int challenge, params int[ ] emloyeeIds )
{

  using ( var connection = new SqlConnection( "my connection string" ) )
  {
    connection.Open( );  // forgot this earlier
    using ( var command = connection.CreateCommand( ) )
    {
      //--> set up command basics...
      command.CommandText = @"
        select 
          given_id, name, message, emplid, category, created_date 
        from 
          GetMyTable( @from, @to, @challenge, @employeeIds )";
      command.CommandType = System.Data.CommandType.Text;

      //--> easy parameters...
      command.Parameters.AddWithValue( "@from", fromDate );
      command.Parameters.AddWithValue( "@to", toDate );
      command.Parameters.AddWithValue( "@challenge", challenge );

      //--> table-valued parameter...
      var table = new DataTable( "EmployeeIds" );
      table.Columns.Add( "Id", typeof( int ) );
      foreach ( var Id in emloyeeIds ) table.Rows.Add( Id );
      var employeeIdsParameter = new SqlParameter( "@employeeIds", SqlDbType.Structured );
      employeeIdsParameter.TypeName = "dbo.IntIdsType";
      employeeIdsParameter.Value = table;
      command.Parameters.Add( employeeIdsParameter );

      //--> do the work...
      using ( var reader = await command.ExecuteReaderAsync( ) )
      {
        while ( await reader.ReadAsync( ) )
        {
          //...
        }
      }
    }
  }
}

An advantage of all this - besides the hoped-for performance gains - you avoid weird logic to build out the SQL strings and the SQL injection attack surface that approach brings.

EDIT:

In light of the comments: In the c# code, it's not returning a string to a caller - it's showing the setup of the command object, how to add parameters to it, and then how to execute it. This is probably currently being done in the procedure that is calling your string-building code now. This is a fairly typical setup for things like this - but there are lots of variations. For example, the calling code could hand you the command object, and you fill it out. Lots of different patterns get employed.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related