有没有比在开头使用1 = 1更好的方法来动态构建SQL WHERE子句?

RRM*_*RRM 109 c# sql

我正在用C#构建一些SQL查询.它将根据代码中存储为变量的某些条件而有所不同.

string Query="SELECT * FROM Table1 WHERE 1=1 ";
if (condition1) 
    Query += "AND Col1=0 ";
if (condition2) 
    Query += "AND Col2=1 ";
if (condition3) 
    Query += "AND Col3=2 ";
Run Code Online (Sandbox Code Playgroud)

它有效,但测试1 = 1似乎并不优雅.如果我没有使用它,我必须记住并检查每次是否已经添加"where"关键字到查询.

有更好的解决方案吗?

Ahm*_*IEM 157

将条件保存在列表中:

List<string> conditions = new List<string>();

if (condition1) conditions.Add("Col1=0");
//...
if (conditions.Any())
    Query += " WHERE " + string.Join(" AND ", conditions.ToArray());
Run Code Online (Sandbox Code Playgroud)

  • 我对这提供的所有SQL注入机会感到兴奋. (101认同)
  • 很好的解决方案,但.NET 4不需要`ToArray()`,因为有一个重载接受任何`IEnumerable <string>`. (24认同)
  • @Jeff如果您没有对where子句中的值进行硬编码,那么您也可以使用SqlParameters获得第二个列表.您只需要在条件列表的同时填充该列表并调用[AddRange(parameters.ToArray())](http://msdn.microsoft.com/en-us/library/0e42546a.aspx)结束. (11认同)
  • @ScottChamberlain是的,您还可以在将输入字符串放入列表之前简单地转义输入字符串.我大多只是警告使用诙谐的幽默来防止可能的攻击. (5认同)
  • @Jeff如果条件包括用户输入(原始示例没有),它只容易受到SQL注入攻击 (4认同)
  • 我同意解决方案,但我不喜欢Query + =,你应该使用string.format (2认同)

Cod*_*ter 85

一种解决方案是不通过附加字符串手动编写查询.您可以使用ORM(如Entity Framework)和LINQ to Entities使用语言和框架为您提供的功能:

using (var dbContext = new MyDbContext())
{
    IQueryable<Table1Item> query = dbContext.Table1;

    if (condition1)
    {
        query = query.Where(c => c.Col1 == 0);
    }
    if (condition2)
    {
        query = query.Where(c => c.Col2 == 1);
    }
    if (condition3)
    {
        query = query.Where(c => c.Col3 == 2);
    }   

    PrintResults(query);
}
Run Code Online (Sandbox Code Playgroud)


Ala*_*ber 17

在这个简单的情况下稍微有点过分,但我过去使用的代码与此类似.

创建一个功能

string AddCondition(string clause, string appender, string condition)
{
    if (clause.Length <= 0)
    {
        return String.Format("WHERE {0}",condition);
    }
    return string.Format("{0} {1} {2}", clause, appender, condition);
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它

string query = "SELECT * FROM Table1 {0}";
string whereClause = string.Empty;

if (condition 1)
    whereClause = AddCondition(whereClause, "AND", "Col=1");

if (condition 2)
    whereClause = AddCondition(whereClause, "AND", "Col2=2");

string finalQuery = String.Format(query, whereClause);
Run Code Online (Sandbox Code Playgroud)

这样,如果没有找到条件,你甚至不打扰在查询中加载where语句,并在解析sql语句时保存sql server一小秒处理垃圾where子句.


Dar*_*usz 15

还有另一种解决方案,也可能不优雅,但可以解决问题:

String query = "SELECT * FROM Table1";
List<string> conditions = new List<string>();
// ... fill the conditions
string joiner = " WHERE ";
foreach (string condition in conditions) {
  query += joiner + condition;
  joiner = " AND "
}
Run Code Online (Sandbox Code Playgroud)

对于:

  • 空条件清单,结果很简单SELECT * FROM Table1,
  • 它将是一个单一的条件 SELECT * FROM Table1 WHERE cond1
  • 每个以下条件将产生额外的 AND condN

  • 如果没有谓词,那就留下了悬空的"WHERE"; 特别存在1 = 1以避免这种情况. (6认同)

jga*_*fin 10

做这样的事情:

using (var command = connection.CreateCommand())
{
    command.CommandText = "SELECT * FROM Table1";

    var conditions = "";
    if (condition1)
    {    
        conditions += "Col1=@val1 AND ";
        command.AddParameter("val1", 1);
    }
    if (condition2)
    {    
        conditions += "Col2=@val2 AND ";
        command.AddParameter("val2", 1);
    }
    if (condition3)
    {    
        conditions += "Col3=@val3 AND ";
        command.AddParameter("val3", 1);
    }
    if (conditions != "")
        command.CommandText += " WHERE " + conditions.Remove(conditions.Length - 5);
}
Run Code Online (Sandbox Code Playgroud)

这是SQL注入安全恕我直言,它很干净.在Remove()简单地删除最后AND;

如果没有设置条件,设置了一个条件或者设置了多个条件,它都可以工作.


Ans*_*man 8

用这个:

string Query="SELECT * FROM Table1 WHERE ";
string QuerySub;
if (condition1) QuerySub+="AND Col1=0 ";
if (condition2) QuerySub+="AND Col2=1 ";
if (condition3) QuerySub+="AND Col3=2 ";

if (QuerySub.StartsWith("AND"))
    QuerySub = QuerySub.TrimStart("AND".ToCharArray());

Query = Query + QuerySub;

if (Query.EndsWith("WHERE "))
    Query = Query.TrimEnd("WHERE ".ToCharArray());
Run Code Online (Sandbox Code Playgroud)

  • 丑陋地狱:)加上它可以创建多达7个字符串,如果我正确计数 (8认同)
  • 有一个错误.纠正了它.如果没有条件存在,我的查询就会遭到轰炸:-P我仍然必须说Ahmed's或CodeCaster对我来说是最好的解决方案.我只为你们提供了另一种选择! (3认同)

mil*_*sma 6

只需在后面添加两条线.

string Query="SELECT * FROM Table1 WHERE 1=1 ";
if (condition1) Query+="AND Col1=0 ";
if (condition2) Query+="AND Col2=1 ";
if (condition3) Query+="AND Col3=2 ";
Query.Replace("1=1 AND ", "");
Query.Replace(" WHERE 1=1 ", "");
Run Code Online (Sandbox Code Playgroud)

例如

SELECT * FROM Table1 WHERE 1=1 AND Col1=0 AND Col2=1 AND Col3=2 
Run Code Online (Sandbox Code Playgroud)

将成为

SELECT * FROM Table1 WHERE Col1=0 AND Col2=1 AND Col3=2 
Run Code Online (Sandbox Code Playgroud)

SELECT * FROM Table1 WHERE 1=1 
Run Code Online (Sandbox Code Playgroud)

将成为

SELECT * FROM Table1
Run Code Online (Sandbox Code Playgroud)

=====================================

感谢您指出此解决方案的缺陷:

"如果由于任何原因,其中一个条件包含文本"1 = 1 AND"或"WHERE 1 = 1",则可能会破坏查询.如果条件包含子查询或尝试检查是否存在某些子查询,则可能出现这种情况例如,列包含此文本.也许这不是您的问题,但您应该记住它......"

为了摆脱这个问题,我们需要区分"主" WHERE 1 = 1和子查询中的那些,这很容易:

只需使"主要" WHERE特别:我会附加一个"$"符号

string Query="SELECT * FROM Table1 WHERE$ 1=1 ";
if (condition1) Query+="AND Col1=0 ";
if (condition2) Query+="AND Col2=1 ";
if (condition3) Query+="AND Col3=2 ";
Run Code Online (Sandbox Code Playgroud)

然后还附加两行:

Query.Replace("WHERE$ 1=1 AND ", "WHERE ");
Query.Replace(" WHERE$ 1=1 ", "");
Run Code Online (Sandbox Code Playgroud)


mck*_*ejm 5

如果这是SQL Server,您可以使此代码更清晰。

这也假设了已知数量的参数,当我考虑可能性时,这可能是一个糟糕的假设。

在 C# 中,您将使用:

using (SqlConnection conn = new SqlConnection("connection string"))
{
    conn.Open();
    SqlCommand command = new SqlCommand()
    {
        CommandText = "dbo.sample_proc",
        Connection = conn,
        CommandType = CommandType.StoredProcedure
    };

    if (condition1)
        command.Parameters.Add(new SqlParameter("Condition1", condition1Value));
    if (condition2)
        command.Parameters.Add(new SqlParameter("Condition2", condition2Value));
    if (condition3)
        command.Parameters.Add(new SqlParameter("Condition3", condition3Value));

    IDataReader reader = command.ExecuteReader();

    while(reader.Read())
    {
    }

    conn.Close();
}
Run Code Online (Sandbox Code Playgroud)

然后在 SQL 端:

CREATE PROCEDURE dbo.sample_proc
(
    --using varchar(50) generically
    -- "= NULL" makes them all optional parameters
    @Condition1 varchar(50) = NULL
    @Condition2 varchar(50) = NULL
    @Condition3 varchar(50) = NULL
)
AS
BEGIN
    /*
    check that the value of the parameter 
    matches the related column or that the 
    parameter value was not specified.  This
    works as long as you are not querying for 
    a specific column to be null.*/
    SELECT *
    FROM SampleTable
    WHERE (Col1 = @Condition1 OR @Condition1 IS NULL)
    AND   (Col2 = @Condition2 OR @Condition2 IS NULL)
    AND   (Col3 = @Condition3 OR @Condition3 IS NULL)
    OPTION (RECOMPILE)
    --OPTION(RECOMPILE) forces the query plan to remain effectively uncached
END
Run Code Online (Sandbox Code Playgroud)


amd*_*amd 5

为什么不使用现有的查询生成器?像Sql Kata之类的东西。

它支持复杂的条件,联接和子查询。

var query = new Query("Users").Where("Score", ">", 100).OrderByDesc("Score").Limit(100);

if(onlyActive)
{
   query.Where("Status", "active")
}

// or you can use the when statement

query.When(onlyActive, q => q.Where("Status", "active"))
Run Code Online (Sandbox Code Playgroud)

它适用于Sql Server,MySql和PostgreSql。