如何在 C# 中创建动态参数化 SQL 查询字符串

How to create a Dynamic Parameterized SQL Query String in C#

我最终试图将我存储在函数应用程序中的并发列表中的一些记录保存到具有参数化值的 SQL 服务器数据库中,其中一个是加密列(不是确定是否相关)。

目前,我正在遍历每条记录并提取值,对它们进行参数化,然后将它们保存到数据库中;为每条记录执行此操作。虽然,据我所知,这是非常低效的,并且有人告诉我用每条记录的参数化值创建一个字符串,然后 运行 该单个字符串作为 SQL 服务器查询会更有效率。

有人可以向我解释一下我是如何实现这样的目标的吗?或者我是否可能弄错了?

谢谢!

[C#]

您可以使用 Table-valued parameters 在单个 SQL 查询中发送多行。 流量将是

  • 定义一个table type。架构将与要插入的参数相同。
  • 使用与 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();

如果要插入的记录很多,您可以创建多个批次并将它们插入循环本身。

以防万一它对以后的人有帮助,我最终解决了这个问题。

使用 SqlBulkCopy 的原因是因为单独的 TVP 似乎不兼容保存到包含 Always Encrypted Column 的 SQL table。尽管类型定义相同,但我收到了一些奇怪的操作数冲突错误。 DataTable 和 TVP 似乎不能很好地与加密列一起使用。

将 SqlBulkCopy 与 TVP 一起使用似乎是允许我们将数据从 C# 脚本保存到具有始终加密列的 SQL table 的解决方法,使用 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();

}