有没有更好的方式来dynamic构buildSQL WHERE子句,而不是在开头使用1 = 1?

我在C#中build立一些SQL查询。 它会根据代码中作为variables存储的一些条件而有所不同。

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 "; 

它工作,但testing1 = 1似乎并不高雅。 如果我没有使用它,我将不得不记住,每次检查“where”关键字是否已经添加或不查询。

有更好的解决scheme吗?

将条件保存在列表中:

 List<string> conditions = new List<string>(); if (condition1) conditions.Add("Col1=0"); //... if (conditions.Any()) Query += " WHERE " + string.Join(" AND ", conditions.ToArray()); 

一种解决scheme是不通过附加string来手动编写查询。 您可以像entity framework一样使用ORM,而使用LINQ to Entities使用语言和框架为您提供的function:

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

在这个简单的例子中有一点矫枉过正,但是我过去使用的代码与此类似。

创build一个函数

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

像这样使用它

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

这样,如果没有发现任何条件,你甚至不会在查询中加载一个where语句,并且在parsingsql语句的时候,保存sql server处理junk where子句的微秒。

还有另一种解决scheme,可能不够高雅,但是可以解决问题:

 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 " } 

对于:

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

用这个:

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

只要做这样的事情:

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

这是SQL注入安全和恕我直言 ,这是相当干净的。 Remove()只是删除最后一个AND ;

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

对于你所要求的我可以想到的最快的文字解决scheme是这样的:

 string Query="SELECT * FROM Table1"; string Conditions = ""; if (condition1) Conditions+="AND Col1=0 "; if (condition2) Conditions+="AND Col2=1 "; if (condition3) Conditions+="AND Col3=2 "; if (Conditions.Length > 0) Query+=" WHERE " + Conditions.Substring(3); 

当然,我会向你推荐CodeCaster使用ORM的build议。 但是如果你想到这里做的是什么,你真的不担心“浪费”4个字符的内存,而且电脑移动指针4个地方真的很快。

如果你有时间学习如何使用ORM,它可以真正为你付出代价。 但是对于这个问题,如果你试图保持SQL db的附加条件,这将为你做。

只需在后面追加两行。

 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 ", ""); 

例如

 SELECT * FROM Table1 WHERE 1=1 AND Col1=0 AND Col2=1 AND Col3=2 

将成为

 SELECT * FROM Table1 WHERE Col1=0 AND Col2=1 AND Col3=2 

 SELECT * FROM Table1 WHERE 1=1 

将成为

 SELECT * FROM Table1 

根据条件,可能在查询中使用布尔逻辑。 像这样的东西:

 string Query="SELECT * FROM Table1 " + "WHERE (condition1 = @test1 AND Col1=0) "+ "AND (condition2 = @test2 AND Col2=1) "+ "AND (condition3 = @test3 AND Col3=2) "; 

如果这是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(); } 

然后在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 

我喜欢stringbuilder的stream畅接口,所以我做了一些ExtensionMethods。

 var query = new StringBuilder() .AppendLine("SELECT * FROM products") .AppendWhereIf(!String.IsNullOrEmpty(name), "name LIKE @name") .AppendWhereIf(category.HasValue, "category = @category") .AppendWhere("Deleted = @deleted") .ToString(); var p_name = GetParameter("@name", name); var p_category = GetParameter("@category", category); var p_deleted = GetParameter("@deleted", false); var result = ExecuteDataTable(query, p_name, p_category, p_deleted); // in a seperate static class for extensionmethods public StringBuilder AppendLineIf(this StringBuilder sb, bool condition, string value) { if(condition) sb.AppendLine(value); return sb; } public StringBuilder AppendWhereIf(this StringBuilder sb, bool condition, string value) { if (condition) sb.AppendLineIf(condition, sb.HasWhere() ? " AND " : " WHERE " + value); return sb; } public StringBuilder AppendWhere(this StringBuilder sb, string value) { sb.AppendWhereIf(true, value); return sb; } public bool HasWhere(this StringBuilder sb) { var seperator = new string [] { Environment.NewLine }; var lines = sb.ToString().Split(seperator, StringSplitOptions.None); return lines.Count > 0 && lines[lines.Count - 1].Contains("where", StringComparison.InvariantCultureIgnoreCase); } // http://stackoverflow.com/a/4217362/98491 public static bool Contains(this string source, string toCheck, StringComparison comp) { return source.IndexOf(toCheck, comp) >= 0; } 

恕我直言,我认为你的做法是错误的:

通过连接string来查询数据库从不是一个好主意 ( SQL注入的风险,如果你在其他地方做一些改变,代码很容易被破坏)。

你可以使用ORM (我使用NHibernate )或者至less使用SqlCommand.Parameters

如果你绝对想使用string连接,我会使用一个StringBuilder (它是string连接的正确对象):

 var query = new StringBuilder("SELECT * FROM Table1 WHERE"); int qLength = query.Length;//if you don't want to count :D if (Condition1) query.Append(" Col1=0 AND"); if (Condition2) query.Append(" Col2=0 AND"); .... //if no condition remove WHERE or AND from query query.Length -= query.Length == qLength ? 6 : 4; 

作为最后一个想法, Where 1=1真的很难看,但SQL Server将无论如何优化它。

我看到这一直在Oracle中使用,而在存储过程中构builddynamicSQL。 我在查询中使用它,同时探索数据问题,以及更快地切换不同的数据filter…只需注释一个条件或轻松地添加它。

我发现这是非常普遍和容易理解的人审查你的代码。

 public static class Ext { public static string addCondition(this string str, bool condition, string statement) { if (!condition) return str; return str + (!str.Contains(" WHERE ") ? " WHERE " : " ") + statement; } public static string cleanCondition(this string str) { if (!str.Contains(" WHERE ")) return str; return str.Replace(" WHERE AND ", " WHERE ").Replace(" WHERE OR ", " WHERE "); } } 

用扩展方法实现。

  static void Main(string[] args) { string Query = "SELECT * FROM Table1"; Query = Query.addCondition(true == false, "AND Column1 = 5") .addCondition(18 > 17, "AND Column2 = 7") .addCondition(42 == 1, "OR Column3 IN (5, 7, 9)") .addCondition(5 % 1 > 1 - 4, "AND Column4 = 67") .addCondition(Object.Equals(5, 5), "OR Column5 >= 0") .cleanCondition(); Console.WriteLine(Query); } 

小巧的SqlBuilder是一个不错的select。 它甚至用于StackOverflow的生产。

阅读山姆的博客条目 。

据我所知,它不是任何Nuget包的一部分,所以你需要复制粘贴它的代码到你的项目中,或者下载Dapper源代码并构buildSqlBuilder项目。 无论如何,您还需要为DynamicParameters类引用Dapper。

为什么不使用现有的查询生成器? 像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")) 

据我所知它适用于Sql Server,MySql和PostgreSql。

使用string函数你也可以这样做:

 string Query = "select * from Table1"; if (condition1) WhereClause += " Col1 = @param1 AND "; // <---- put conditional operator at the end if (condition2) WhereClause += " Col1 = @param2 OR "; WhereClause = WhereClause.Trim(); if (!string.IsNullOrEmpty(WhereClause)) Query = Query + " WHERE " + WhereClause.Remove(WhereClause.LastIndexOf(" ")); // else // no condition meets the criteria leave the QUERY without a WHERE clause 

我个人觉得很容易删除最后的条件元素,因为它的位置很容易预测。

我想到了一个解决scheme,好吧,可能更具可读性:

 string query = String.Format("SELECT * FROM Table1 WHERE " + "Col1 = {0} AND " + "Col2 = {1} AND " + "Col3 = {2}", (!condition1 ? "Col1" : "0"), (!condition2 ? "Col2" : "1"), (!condition3 ? "Col3" : "2")); 

我只是不确定SQL解释器是否也会优化Col1 = Col1条件(当condition1为false时打印)。

这是一个更优雅的方式:

  private string BuildQuery() { string MethodResult = ""; try { StringBuilder sb = new StringBuilder(); sb.Append("SELECT * FROM Table1"); List<string> Clauses = new List<string>(); Clauses.Add("Col1 = 0"); Clauses.Add("Col2 = 1"); Clauses.Add("Col3 = 2"); bool FirstPass = true; if(Clauses != null && Clauses.Count > 0) { foreach(string Clause in Clauses) { if (FirstPass) { sb.Append(" WHERE "); FirstPass = false; } else { sb.Append(" AND "); } sb.Append(Clause); } } MethodResult = sb.ToString(); } catch //(Exception ex) { //ex.HandleException() } return MethodResult; } 

如前所述, 通过连接创buildSQL从来就不是一个好主意 。 不仅仅是因为SQL注入。 主要是因为它只是丑陋,难以维护,完全没有必要 。 您必须通过跟踪或debugging来运行程序,以查看它生成的SQL。 如果你使用QueryFirst (免责声明:我写的),不愉快的诱惑被删除,你可以直接在SQL中做它。

本页面全面介绍了用于dynamic添加search谓词的TSQL选项。 以下选项适用于您希望将search谓词的组合select留给用户的情况。

 select * from table1 where (col1 = @param1 or @param1 is null) and (col2 = @param2 or @param2 is null) and (col3 = @param3 or @param3 is null) OPTION (RECOMPILE) 

QueryFirst将C#null赋给了db NULL,所以你可以在适当的时候用空值调用Execute()方法,这一切都可以。 <意见>为什么C#开发人员不愿意在SQL中做东西,即使它更简单。 心灵浮华。</ opinion>

对于更长的过滤步骤,StringBuilder是许多人所说的更好的方法。

在你的情况下,我会去:

 StringBuilder sql = new StringBuilder(); if (condition1) sql.Append("AND Col1=0 "); if (condition2) sql.Append("AND Col2=1 "); if (condition3) sql.Append("AND Col3=2 "); string Query = "SELECT * FROM Table1 "; if(sql.Length > 0) Query += string.Concat("WHERE ", sql.ToString().Substring(4)); //avoid first 4 chars, which is the 1st "AND "