在Microsoft.Data.SqlClient中实现WHERE子句匹配多个手机号的最优方案
在Microsoft.Data.SqlClient中实现WHERE子句匹配多个手机号的最优方案
嘿,兄弟,别想着用循环一个个拼条件或者重复执行查询了——不仅效率低,还容易踩SQL注入的大坑!针对MS SQL Server,我们有更优雅、安全且高效的方法来处理WHERE子句匹配多个手机号的场景,下面给你拆解几个靠谱的方案:
1. 表值参数(TVP)——最推荐的最优解
这是SQL Server和.NET配合处理多值参数的黄金方案,性能拉满还完全避免注入风险,适合批量数据场景。
步骤1:先在SQL Server中创建自定义表类型
CREATE TYPE PhoneNumberList AS TABLE (PhoneNumber VARCHAR(20))
步骤2:C#代码中构造表参数并执行查询
// 你的目标手机号列表 var phoneNumbers = new List<string> { "+7897654321", "+7878765444" }; // 构造符合自定义表类型的DataTable var phoneTable = new DataTable(); phoneTable.Columns.Add("PhoneNumber", typeof(string)); foreach (var phone in phoneNumbers) { phoneTable.Rows.Add(phone); } string sqlExpression = @" select d.* from MobilePhone m join [User] u on m.User_IDREF = u.User_ID join Device d on u.User_ID = d.User_ID WHERE m.MobilePhone IN (SELECT PhoneNumber FROM @PhoneNumbers);"; using (SqlConnection connection = new SqlConnection(connectionString)) { await connection.OpenAsync(); using (SqlCommand command = new SqlCommand(sqlExpression, connection)) { // 添加表值参数,指定类型为自定义的PhoneNumberList var tvpParam = command.Parameters.Add("@PhoneNumbers", SqlDbType.Structured); tvpParam.TypeName = "PhoneNumberList"; tvpParam.Value = phoneTable; using (SqlDataReader reader = await command.ExecuteReaderAsync()) { // 处理查询结果 while (await reader.ReadAsync()) { // 这里写读取数据的逻辑 } } } }
2. STRING_SPLIT函数——快捷轻量方案(SQL Server 2016+支持)
如果不想创建自定义表类型,用内置的STRING_SPLIT函数也能搞定,适合小批量手机号的场景。
var phoneNumbers = new List<string> { "+7897654321", "+7878765444" }; // 把手机号拼成逗号分隔的字符串 var phoneListStr = string.Join(",", phoneNumbers); string sqlExpression = @" select d.* from MobilePhone m join [User] u on m.User_IDREF = u.User_ID join Device d on u.User_ID = d.User_ID WHERE m.MobilePhone IN (SELECT value FROM STRING_SPLIT(@PhoneNumberList, ','));"; using (SqlConnection connection = new SqlConnection(connectionString)) { await connection.OpenAsync(); using (SqlCommand command = new SqlCommand(sqlExpression, connection)) { command.Parameters.Add("@PhoneNumberList", SqlDbType.VarChar).Value = phoneListStr; using (SqlDataReader reader = await command.ExecuteReaderAsync()) { // 处理结果逻辑 } } }
注意:如果手机号里可能包含逗号(虽然大概率不会),可以换个不冲突的分隔符,比如|或者;
3. 动态参数化SQL——迫不得已的备选
如果以上两种方案都没法用(比如老版本SQL Server),可以用动态生成参数的方式,绝对不要直接拼字符串! 一定要保持参数化避免注入:
var phoneNumbers = new List<string> { "+7897654321", "+7878765444" }; // 动态生成参数占位符,比如@Phone0, @Phone1... var paramPlaceholders = new List<string>(); for (int i = 0; i < phoneNumbers.Count; i++) { paramPlaceholders.Add($"@Phone{i}"); } string sqlExpression = $@" select d.* from MobilePhone m join [User] u on m.User_IDREF = u.User_ID join Device d on u.User_ID = d.User_ID WHERE m.MobilePhone IN ({string.Join(", ", paramPlaceholders)});"; using (SqlConnection connection = new SqlConnection(connectionString)) { await connection.OpenAsync(); using (SqlCommand command = new SqlCommand(sqlExpression, connection)) { // 逐个添加参数 for (int i = 0; i < phoneNumbers.Count; i++) { command.Parameters.Add($"@Phone{i}", SqlDbType.VarChar).Value = phoneNumbers[i]; } using (SqlDataReader reader = await command.ExecuteReaderAsync()) { // 处理结果逻辑 } } }
方案总结
- 优先选表值参数:性能最优,支持大数据量,完全安全;
- 小批量场景选STRING_SPLIT:无需提前创建对象,代码更简洁;
- 动态参数化SQL仅作为最后备选,尽量少用。
备注:内容来源于stack exchange,提问作者Timur
相关产品推荐
相关产品推荐

