如何在 C# 中创建动态参数化 SQL 查询字符串
Posted
技术标签:
【中文标题】如何在 C# 中创建动态参数化 SQL 查询字符串【英文标题】:How to create a Dynamic Parameterized SQL Query String in C# 【发布时间】:2022-01-06 20:24:05 【问题描述】:我最终试图将存储在我的 Function App 内的并发列表中的一些记录保存到带有参数化值的 SQL Server 数据库中,其中一个是加密列(不确定是否相关)。
目前,我正在遍历每条记录并提取值、参数化它们并将它们保存到数据库中;为每条记录执行此操作。不过,据我了解,这是非常低效的,我被告知使用每条记录的参数化值创建一个字符串,然后将该单个字符串作为 SQL Server 查询运行会更有效。
有人可以向我解释一下我是如何实现这样的目标的吗?或者我是否弄错了?
谢谢!
【问题讨论】:
使用表值参数一次性发送多条记录。 表值参数是我的首选,但对于 2016+,我倾向于选择传递 JSON。它提供了更多的灵活性。 【参考方案1】:以防万一它对将来的任何人有所帮助,我最终解决了这个问题。
使用 SqlBulkCopy 的原因是因为单独的 TVP 似乎与保存到包含 Always Encrypted 列的 SQL 表不兼容。尽管类型定义相同,但我收到了一些奇怪的操作数冲突错误。 DataTable 和 TVP 似乎不能很好地处理加密列。
将 SqlBulkCopy 与 TVP 一起使用似乎是一种解决方法,它允许我们将 C# 脚本中的数据保存到具有始终加密列的 SQL 表中,将 TVP 用于单个查询语句以加快处理时间。
using (SqlConnection connection = new SqlConnection(<connection_string>))
connection.AccessToken = new VisualStudioCredential().GetToken()
if (connection.State == ConnectionState.Closed)
connection.Open();
// Create a new DataTable - !Important - We also have to Create Table Type in our Database as mentioned in this thread.
DataTable saveBatchTable = new DataTable();
// Add rows to the DataTable
saveBatchTable.Columns.Add("col_1", typeof(string));
saveBatchTable.Columns.Add("col_2", typeof(long));
saveBatchTable.Columns.Add("col_3", typeof(bool));
// Assuming we have a list of records - for each 'record' in our list...
foreach (custObject record in recordList)
// and for each index attribute value within each record object...
foreach (long idx in record.index)
// Add a row of values corresponding to the columns previously added
saveBatchTable.Rows.Add(record.col1Val, record.col2Val, true)
saveBatchTable.AcceptChanges();
string stagingTableName = "MyStagingTable"
// Create an empty temporary table with the headers from your Source Table within your DB
using (var cmd = new SqlCommand("SELECT * INTO [" + stagingTableName + "] FROM [<SourceTableName>] WHERE 1 = 2;", connection))
cmd.ExecuteNonQuery();
// Write the DataTable (saveBatchTable) to the new temporary table
using (var bulkCopy = new SqlBulkCopy(connection))
bulkCopy.DesintationTableName = "[" + stagingTableName + "]";
bulkCopy.WriteToServer(saveBatchTable);
// Store the data within the temporary table to our Source Table then drop the temp table
using (var cmd = new SqlCommand("BEGIN TRAN; INSERT [<SourceTableName>] SELECT * FROM [" + stagingTableName + "]; DROP TABLE [" + stagingTableName + "]; COMMIT", connection))
Console.WriteLine("Number of Rows Affected = " + cmd.ExecuteNonQuery().ToString());
connection.Close();
【讨论】:
【参考方案2】:[C#
]
您可以使用Table-valued parameters
在单个 SQL 查询中发送多行。
流程将是
table type
。架构将与要插入的参数相同。
使用与table type
完全相同的名称和类型创建一个DataTable
。
在查询中将DataTable
作为参数传递。
样本
CREATE TYPE MyTableType AS TABLE
( mytext TEXT,
num INT );
using (SqlConnection connection = new SqlConnection(CloudConfigurationManager.GetSetting("Sql.ConnectionString")))
connection.Open();
DataTable table = new DataTable();
// Add columns and rows. The following is a simple example.
table.Columns.Add("mytext", typeof(string));
table.Columns.Add("num", typeof(int));
for (var i = 0; i < 10; i++)
table.Rows.Add(DateTime.Now.ToString(), DateTime.Now.Millisecond);
SqlCommand cmd = new SqlCommand(
"INSERT INTO MyTable(mytext, num) SELECT mytext, num FROM @TestTvp",
connection);
cmd.Parameters.Add(
new SqlParameter()
ParameterName = "@TestTvp",
SqlDbType = SqlDbType.Structured,
TypeName = "MyTableType",
Value = table,
Direction = ParameterDirection.Input,
);
cmd.ExecuteNonQuery();
参考:https://docs.microsoft.com/en-us/azure/azure-sql/performance-improve-use-batching#table-valued-parameters
[JAVA
]
您可以使用PreparedStatement
,创建一批要插入的行(ps.addBatch()
),然后一次性插入一批(ps.executeBatch()
)。
示例:
PreparedStatement ps= con.prepareStatement("INSERT INTO Sample VALUES (?, ?, ?, ?)");
for(int i; i<10; i++)
ps.setString(1, "String1");
ps.setString(2, "String2");
ps.setString(3, "String3");
ps.setInt(4, 1000);
ps.addBatch();
ps.executeBatch();
如果要插入的记录很多,您可以创建多个批次并将它们插入到循环本身中。
【讨论】:
@Dai 感谢您的指出。我现在已经更正了答案。 非常有帮助!非常感谢您花时间向我解释这一点! 如果我已经有一个现有的表,我正在尝试将这个新 DataTable 中的记录保存到其中,大概我不需要 CREATE TYPE MyTableType AS TABLE - 还是我弄错了?我是否必须根据要保存到的现有表创建新表类型?谢谢! @Klh 是的,您必须创建一个table type
。值将存储在创建的table type
中,并将在插入语句中传递。请参考上面的示例并尝试创建示例。它会让你更清楚。
谢谢!当我意识到我需要创建一个新类型时,我昨天能够让它工作。 - 我只是在插入加密列值时遇到问题,因为当我只是使用参数化值时,我指定了 varchar(100) 或 varchar(200),但是,现在我已经在我的表类型中指定了这些但我与 varchar 和 varchar(100) 存在冲突问题,由于某种原因,这是我的加密列。它们在表类型中的定义与我尝试插入的表中的定义相同,因此我不太确定存在什么问题。以上是关于如何在 C# 中创建动态参数化 SQL 查询字符串的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 C# 在 SQL Server 数据库中创建视图?