为什么我们总是喜欢在SQL语句中使用参数?

我对使用数据库非常新。 现在我可以编写SELECTUPDATEDELETEINSERT命令。 但是我看过很多论坛,我们喜欢写:

 SELECT empSalary from employee where salary = @salary 

…代替:

 SELECT empSalary from employee where salary = txtSalary.Text 

为什么我们总是喜欢使用参数,我将如何使用它们?

我想知道第一种方法的用途和好处。 我甚至听说过SQL注入,但我不完全明白这一点。 我甚至不知道SQL注入是否与我的问题有关。

当数据库与程序接口(如桌面程序或网站)结合使用时,使用参数有助于防止SQL注入攻击

在您的示例中,用户可以通过在txtSalarytxtSalary语句直接在数据库上运行SQL代码。

例如,如果他们写0 OR 1=1 ,那么执行的SQL就是

  SELECT empSalary from employee where salary = 0 or 1=1 

由此所有的雇员将被退回。

而且,用户可以对数据库执行更糟糕的命令,包括删除它们0; Drop Table employee 0; Drop Table employee

 SELECT empSalary from employee where salary = 0; Drop Table employee 

employee将被删除。


在你的情况下,它看起来像你使用.NET。 使用参数非常简单:

C#

 string sql = "SELECT empSalary from employee where salary = @salary"; using (SqlConnection connection = new SqlConnection(/* connection info */)) using (SqlCommand command = new SqlCommand(sql, connection)) { var salaryParam = new SqlParameter("salary", SqlDbType.Money); salaryParam.Value = txtMoney.Text; command.Parameters.Add(salaryParam); var results = command.ExecuteReader(); } 

VB.NET

 Dim sql As String = "SELECT empSalary from employee where salary = @salary" Using connection As New SqlConnection("connectionString") Using command As New SqlCommand(sql, connection) Dim salaryParam = New SqlParameter("salary", SqlDbType.Money) salaryParam.Value = txtMoney.Text command.Parameters.Add(salaryParam) Dim results = command.ExecuteReader() End Using End Using 

编辑2016-4-25:

根据George Stocker的评论,我将示例代码更改为不使用AddWithValue 。 此外,通常build议您在using语句中包装IDisposable s。

你是对的,这与SQL注入有关 ,这是一个漏洞,允许一个malicioius用户对你的数据库执行任意语句。 这个旧时最喜欢的XKCD漫画说明了这个概念:

在这里输入图像描述


在你的例子中,如果你只是使用:

 var query = "SELECT empSalary from employee where salary = " + txtSalary.Text; // and proceed to execute this query 

你打开SQL注入。 例如,有人inputtxtSalary:

 1; UPDATE employee SET salary = 9999999 WHERE empID = 10; -- 1; DROP TABLE employee; -- // etc. 

当你执行这个查询时,它将执行一个SELECT和一个UPDATEDROP ,或者任何他们想要的。 最后简单地说出你的查询的其余部分,如果你在txtSalary.Text之后连接了任何东西,这在攻击中会很有用。


正确的方法是使用参数化查询,例如(C#):

 SqlCommand query = new SqlCommand("SELECT empSalary FROM employee WHERE salary = @sal;"); query.Parameters.AddWithValue("@sal", txtSalary.Text); 

有了这个,你可以安全地执行查询。

有关如何避免SQL注入其他几种语言的参考,请查看由SO用户维护的网站bobby-tables.com 。

在Sql中,当任何单词包含@符号时,这意味着它是可变的,我们使用这个variables来设置它的值,并在同一个sql脚本的数字区域使用它,因为它只限于单个脚本,而你可以声明很多variables同一types和名称上的许多脚本。 我们在存储过程中使用这个variables很多,因为存储过程是预编译的查询,我们可以从脚本,桌面和网站传递这些variables的值,以获取进一步的信息: Declare Local Variable , Sql Stored Procedure和sql注入 。

另外阅读保护从SQL注入它将指导你如何保护你的数据库。

希望它能帮助你理解任何问题,评论我。

其他答案涵盖了为什么参数是重要的,但有一个缺点! 在.net中,有几个创build参数(Add,AddWithValue)的方法,但是它们都需要你担心,不必要的参数名称,它们都会降低代码中SQL的可读性。 当你试图冥想SQL时,你需要在上面或下面search,看看在参数中使用了什么值。

我虚心声称我的小SqlBuilder类是编写参数化查询最优雅的方式 。 你的代码看起来像这样

C#

 var bldr = new SqlBuilder( myCommand ); bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId); //or bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName); myCommand.CommandText = bldr.ToString(); 

你的代码将会更短,更可读。 你甚至不需要额外的线路,而且当你读回来的时候,你不需要四处寻找参数的值。 你需要的课是这里…

 using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; public class SqlBuilder { private StringBuilder _rq; private SqlCommand _cmd; private int _seq; public SqlBuilder(SqlCommand cmd) { _rq = new StringBuilder(); _cmd = cmd; _seq = 0; } public SqlBuilder Append(String str) { _rq.Append(str); return this; } public SqlBuilder Value(Object value) { string paramName = "@SqlBuilderParam" + _seq++; _rq.Append(paramName); _cmd.Parameters.AddWithValue(paramName, value); return this; } public SqlBuilder FuzzyValue(Object value) { string paramName = "@SqlBuilderParam" + _seq++; _rq.Append("'%' + " + paramName + " + '%'"); _cmd.Parameters.AddWithValue(paramName, value); return this; } public override string ToString() { return _rq.ToString(); } } 

除了需要添加的其他答案参数不仅有助于防止SQL注入,但可以提高查询的性能 。 Sql服务器caching参数化查询计划,并在重复查询执行时重用它们。 如果你没有参数化你的查询,那么sql server会在每个查询 (有一些排除)的情况下编译新的计划,如果查询的文本不同。

有关查询计划caching的更多信息

两年后我第一次去 ,我正在重新…

为什么我们喜欢参数? SQL注入显然是一个很大的原因,但是我们可能会暗中渴望回到SQL 作为一种语言 。 string文字中的SQL已经是一种奇怪的文化习惯,但至less可以将请求复制并粘贴到pipe理工作室中。 当SQL有条件和控制结构时,用宿主语言条件和控制结构dynamic构build的SQL只是0级的野蛮行为。 您必须在debugging或跟踪中运行您的应用程序,以查看它生成的SQL。

不要只停留在参数。 去所有的方式,并使用QueryFirst (免责声明:我写的)。 你的SQL存在于一个.sql文件中 。 您可以在神话般的TSQL编辑器窗口中进行编辑,为您的表格和列使用语法validation和智能感知。 您可以在特殊注释部分分配testing数据,然后点击“播放”在窗口中运行您的查询。 创build一个参数就像在你的SQL中放置“@myParam”一样简单。 然后,每次保存时,QueryFirst将为您的查询生成C#包装器。 你的参数popup,强types,作为Execute()方法的参数。 您的结果将在IEnumerable或强typesPOCO列表中返回,这些POCO是根据查询返回的实际模式生成的types。 如果您的查询不能运行,您的应用程序将无法编译。 如果您的数据库模式发生变化并且您的查询运行但某些列消失,则编译错误将指向您的代码中尝试访问缺失数据的行。 还有许多其他的优点。 你为什么要以其他方式访问数据?

旧post,但希望确保新手知道存储过程。

我的10c值得这里是,如果你能写你的SQL语句作为存储过程,在我看来是最佳的方法。 我总是使用存储的特效,并永远不会循环通过logging在我的主要代码。 例如:SQL表> SQL存储过程> IIS / Dot.NET>类。

使用存储过程时,只能将用户限制为EXECUTE权限,从而降低安全风险。

您的存储过程本质上是参数化的,您可以指定input和输出参数。

存储过程(如果它通过SELECT语句返回数据)可以像访问代码中的常规SELECT语句一样访问和读取。

它在SQL Server上编译也运行得更快。

我还提到你可以做多个步骤,例如更新表格,检查另一个数据库服务器上的值,然后一旦最终完成,将数据返回给客户端,全部在同一台服务器上,并且不与客户端交互。 所以这比在你的代码中编码这个逻辑要快得多。