Makishima
Makishima

Reputation: 125

C#: Inner Join 4 tables SQL Server

I am joining 4 tables in one SQL statement that reads data into objects and fill a gridview as well.

My question: Is this a good practice ? does it have any side effects like performance when reading from the database? if so please provide me with some tips in improving it.

protected void OrdersGridView_SelectedIndexChanged(object sender, EventArgs e)
{
    string OID = OrdersGridView.SelectedRow.Cells[0].Text;
    OrderIDlbl.Text = "Order# " + OID;

    using (SqlConnection con = new SqlConnection(cData.CS))
    {
        con.Open();
        {
            string sql = "select o.*, c.*, oi.*, p.* from Orders as o INNER JOIN Customers as c ON o.CustID = c.CustomerID INNER JOIN OrderItems as oi ON o.OrderID = oi.InvoiceID INNER JOIN Products as p ON p.PartNumber = oi.PartNumb where OrderID ='" + OID + "'";

            SqlCommand myCommand = new SqlCommand(sql, con);
            myCommand.CommandTimeout = 15;
            myCommand.CommandType = CommandType.Text;

            using (SqlDataReader myReader = myCommand.ExecuteReader())
            {
                while (myReader.Read())
                {
                    passid.Text = (myReader["CustID"].ToString());
                    TermsDropdown.Value = (myReader["PaymentTerms"].ToString());
                    PaymentDate.Value = ((DateTime)myReader["PaymentDate"]).ToString("MMMM dd, yyyy");
                    OrderDate.Value = ((DateTime)myReader["OrderDate"]).ToString("MMMM dd, yyyy");
                    SalesRep.Value = (myReader["SalesRep"].ToString());
                    comenttxtbox.Value = (myReader["Comments"].ToString());
                    Discountlbl.Text = "Discount: " + (myReader["Discount"].ToString() + " AED");
                    Totallbl.Text = "Total: " + (myReader["Total"].ToString() + " AED");
                    Statuslbl.Text = (myReader["OrderStatus"].ToString());
                    SelectCustomertxtbox.Value = (myReader["Company"].ToString());
                    Name.Text = "Name: " + (myReader["FName"].ToString()) + " " + (myReader["LName"].ToString());
                    Phone.Text = "Phone: " + (myReader["Phone"].ToString());
                    Mail.Text = "Mail: " + (myReader["Personal_Email"].ToString());
                }
            }

            DataTable dt = new DataTable();

            using (SqlDataAdapter da = new SqlDataAdapter(myCommand))
            {
                da.Fill(dt);

                OrderItemsGridview.DataSource = dt;
                OrderItemsGridview.EmptyDataText = "No Items";
                OrderItemsGridview.DataBind();
            }
        }
    }
}

Orders POPUP

Upvotes: 3

Views: 401

Answers (5)

CodingYoshi
CodingYoshi

Reputation: 26989

In addition to what Jakotheshadows has answered, before you make the changes as per his suggestion and getting the data in multiple calls (separate queries) ask yourself these questions:

What is more important for you?

  1. Saving the amount of redundant data you are getting? If yes, then proceed with his suggestion.
  2. Is speed more important? If yes, then keep the joins but follow Jakotheshadows's advice and use parameters and remove the aliases etc.

If you get the data in one shot, like you are with joins, it means one trip to the database but more redundant data. If you do separate queries, it is more than 1 trip (1 trip per query) so it can take longer. So make an informative decision.

In simple words, if you are going grocery shopping do you want to get everything in one shot (heavier trip) or make more trips but make them lighter trips. (Though this grocery example does not have redundancy but you get the point...)

Keep in mind that performance (speed) should not be your concern until it has become a bottleneck. But nonetheless, it is good that you know this.

Upvotes: 0

Steve
Steve

Reputation: 11963

It depends on how you query the data. If its only simple SELECT ... FROM ... WHERE ID = X then it is totally fine to do.

But there are cases which such join is going to cause significant performance degrade. Lets say you did something like

SELECT TOP 10 * FROM Table1 INNER JOIN Table2 ON..  
INNER JOIN Table3 ON .. 
INNER JOIN Table4 ON.. 
WHERE Table1.Column1 = 1 AND 
      Table2.Column1 = 2 AND 
      Table3.Column1 = 3 AND 
      Table4.Column1 = 4 
ORDER BY Table1.Column1, Table2.Column1, Table3.Column1, Table4.Column1

This is very unlikely to happen but if you ever come across a case like this, Sql is likely to do a full table scan since indexes can only include columns in its own table but the order by included columns in all 4 tables so does the where clause.

In order to solve such problem you could use a materialized views which you can add indexes to cover all columns. But before you need something complicated like this 4 joins is fine.

Upvotes: 0

Jakotheshadows
Jakotheshadows

Reputation: 1515

I wouldn't necessarily say it is a bad practice to do joins, however you should definitely get rid of the alias.* pattern that is emerging in your queries. Not explicitly selecting which columns you need is a bad practice in code.

As for the join, often it is the only way if you do not have control over the database design. However, you could greatly simplify your in-code SQL query by creating a view which does those joins for you. I would, however, question whether or not an INNER JOIN is what you actually want. Keep in mind an inner join only shows results that contain a match on both sides of the join.

In your particular case, I recommend separating the queries for the order information and the order items themselves. You're adding redundant information by including the order info on every order item. Also, whenever your query requires outside input, make sure to use a parameterized query:

myCommand.CommandText = "select o.Date, o.Title, o.Id from Orders where Id = @Id";
myCommand.Parameters.Add("@Id", SqlDbType.Int);
myCommand.Parameters["@Id"].Value = 3;

Don't forget to get rid of the alias.* stuff in favor of explicitly selecting your columns.

Upvotes: 2

Cam Bruce
Cam Bruce

Reputation: 5689

Only include the necessary columns in your data. You're pulling a lot of extra data that you don't need, which is created extra server & network overhead, and will absolutely make things slower.

Also, you need to use a SqlParameter for any parameters. Your SQL is subject to sql injection.

string sql = "select <only columns you need> from Orders as o INNER JOIN Customers as c ON o.CustID = c.CustomerID INNER JOIN OrderItems as oi ON o.OrderID = oi.InvoiceID INNER JOIN Products as p ON p.PartNumber = oi.PartNumb where OrderID ='@OrderId";
            SqlCommand myCommand = new SqlCommand(sql, con);
            myCommand.CommandTimeout = 15;
            myCommand.CommandType = CommandType.Text;
            myCommand.Parameters.AddWithValue("@OrderId", oid);

Upvotes: 0

Surendra
Surendra

Reputation: 721

I would suggest to create a stored procedure in the database with the argument as Order ID. Then call the stored procedure from the .net instead the direct calling the SQL. This has the below advantages

  1. Removes the SQL Injection security problem.
  2. SQL caches the query plan and executes faster the next time you process it, rather than the adhoc query you are firing.

Upvotes: 0

Related Questions