2016-11-23 100 views
3

我在一个SQL语句中连接4个表,它将数据读入对象并填充gridview。C#:内部连接4个表SQL Server

我的问题:这是一个很好的做法吗?从数据库读取时是否有任何副作用,如性能?如果是这样,请给我提供一些改进它的提示。

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

+0

快速回答。不,你正在拉这么多不需要的数据。唯一重复的数据是项目。所以你有几个查询只返回一条记录。 – gbianchi

+0

只要你有适当的索引,连接本身并不是一件坏事。虽然星列并不是一个好的做法,因为它们会返回比您需要的更多的数据,并且随着时间的推移可能会导致其他问题,因为对数据库模式进行了更改。 – wdosanjos

+0

[SQL注入警报](http://msdn.microsoft.com/en-us/library/ms161953%28v=sql.105%29.aspx) - 您应该**不**将您的SQL语句连接在一起 - 使用**参数化查询**,而不是为了避免SQL注入 –

回答

2

我不一定会说这是一个不好的做法,做连接,但是你一定要摆脱别名。*正在出现在查询模式。不明确选择您需要的列是代码中的不良做法。

至于连接,通常这是唯一的方法,如果你不能控制数据库设计。但是,您可以通过创建一个视图来为您实现这些联接,从而大大简化您的代码内SQL查询。然而,我会问,INNER JOIN是否是你真正想要的。请记住,内部联接仅显示包含联接两侧匹配的结果。

在您的具体情况下,我建议分隔订单信息和订单项目本身的查询。通过在每个订单项目中包含订单信息来添加冗余信息。同时,当您查询需要外部输入,确保使用参数化查询:

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; 

不要忘记摆脱别名*的东西,在赞成的明确选择你的专栏。

+0

tbh我不确定是否需要订购商品和订单表之间的INNER JOIN。你会认为Left Join在这里比较好,因为我从订单商品表中获得一行订单和多行记录? – Makishima

+0

我不确定你需要加入那里。就像我所说的,你应该真的为获取订单信息和订单项目做单独的查询。先获取订单信息,然后获取该订单的物品。用参数化查询来完成这两件事。如果你为此加入了联合,你只是查询不必要的数据。 – Jakotheshadows

+0

谢谢,我会按照你的意见。 – Makishima

0

我会建议使用参数作为订单ID在数据库中创建一个存储过程。然后从.net调用存储过程,而不是直接调用SQL。 这有以下优点

  1. 消除SQL注入安全问题。
  2. SQL缓存查询计划并在下次处理时执行得更快,而不是您正在触发的即席查询。
+1

我会补充说,如果不能使用sprocs,至少需要参数化sql:http://csharp-station.com/Tutorial/AdoDotNet/Lesson06 –

0

只在数据中包含必要的列。您正在拉取大量不需要的额外数据,这会造成额外的服务器网络开销,并且会让事情变得更慢。

此外,您需要为任何参数使用SqlParameter。你的SQL受到sql注入的影响。

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); 
0

这取决于您如何查询数据。如果它只有简单的SELECT ... FROM ... WHERE ID = X,那么这是完全可以做到的。

但有些情况下,这种连接将导致显着的性能下降。比方说,你不喜欢的东西

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 

这是不太可能发生,但如果你曾经遇到过这样的情况下,SQL是有可能做一个全表扫描,因为索引可以只包含它自己的表列,但所有4个表中包含的列的顺序与where子句一样。

为了解决这个问题,你可以使用物化视图,你可以添加索引来覆盖所有的列。但在你需要像这样复杂的事情之前,4个连接都可以。

0

除了什么Jakotheshadows已经回答了,你做出改变之前的按他的建议并得到多次调用的数据(单独的查询)问自己这些问题:

什么对你更重要?

  1. 保存您获得的冗余数据量?如果是,那么继续他的建议。
  2. 速度更重要吗?如果是的话,然后保持连接,但遵循Jakotheshadows的建议和使用参数,并删除别名等。

如果您一次获取数据,就像您使用连接一样,这意味着一次访问数据库,但更多冗余数据。如果您执行单独的查询,则它不止一次(每个查询一次),因此可能需要更长的时间。所以做一个翔实的决定。

简而言之,如果你要去杂货店买东西,你想把所有东西都放在一个镜头里(更重的旅行),或者做更多的旅行,但让它们更轻松一些。 (虽然这个杂货店的例子没有冗余,但你明白了......)

请记住,性能(速度)不应该是你的关注,直到它成为一个瓶颈。但是,你知道这一点很好。