为什么我的ADO.NET查询不会在单个执行中运行,而是会在单独的执行中运行?

时间:2019-12-15 14:29:30

标签: c# sql-server datatable ado.net sqlparameter

关于一个问题及其相关的answer,我正在尝试使用SQL查询创建一个新的表类型,并对该表类型的数据运行联接查询。

我将两个查询都放在相同的脚本中,即create typeselect,但是它不起作用。我的查询如下(整个代码放在问题的末尾):

IF TYPE_ID(N'BranchMappingType') IS NULL 
CREATE TYPE BranchMappingType As Table 
(COL_ServerID int, 
COL_ServerSchema Common._SMALL_, 
COL_TableName Common._SMALL_, 
COL_BranchNo Common._SMALL_, 
COL_BranchSchema Common._SMALL_ ); 

SELECT [VMA_BranchID] FROM [VIP].[VipMapping] 
JOIN @TVP ON 
VMA_ServerID=COL_ServerID  AND 
VMA_ServerSchema=COL_ServerSchema  AND 
VMA_TableName=COL_TableName  AND 
VMA_BranchNo=COL_BranchNo  AND 
VMA_BranchSchema=COL_BranchSchema;

我将使用SqlDataReader.ExecuteReader执行查询,但会引发异常:

  

列,参数或变量@TVP。 :找不到数据类型BranchMappingType。

但是,当我运行查询的第一部分,即IF TYPE_ID(...) ... CREATE TYPE时,它将没有错误地执行,并且然后运行第二部分的查询,即Select from ... join @TVP可以正常工作。

当我一次执行SqlDataReader.ExecuteReader来运行两个查询时,似乎无法识别新创建的表类型。

我想知道原因是什么?在执行过程中是否犯了一些错误?我更喜欢一次调用SqlDataReader.ExecuteReader方法来运行两个查询。

这里有完整的代码,可以更好地进行检查:

List<int> keys = new List<int>();
if (foreignKeyInfoList == null || foreignKeyInfoList.Count == 0)
    return new StatusResponseKeysList { ErrorCode = ErrorCodes.ERROR_CODE_MINUS_019_NULL_OR_EMPTY_INPUT_ARGUMENT };
const string ID_FIELD = "VMA_BranchID";
const string STRUCTURED_DATA_TABLE_NAME = "@TVP";
const string COL1_SERVER_ID = "COL_ServerID";
const string COL2_SERVER_SCHEMA = "COL_ServerSchema";
const string COL3_TABLE_NAME = "COL_TableName";
const string COL4_BRANCH_NO = "COL_BranchNo";
const string COL5_BRANCH_SCHEMA = "COL_BranchSchema";
//const string COL6_TABLE_ID = "COL_TableID";
const string MAP1_SERVER_ID = "VMA_ServerID";
const string MAP2_SERVER_SCHEMA = "VMA_ServerSchema";
const string MAP3_TABLE_NAME = "VMA_TableName";
const string MAP4_BRANCH_NO = "VMA_BranchNo";
const string MAP5_BRANCH_SCHEMA = "VMA_BranchSchema";
const string TYPE_NAME = "BranchMappingType";
//const string MAP6_TABLE_ID = "VMA_TableID";
string sqlQuery = $"IF TYPE_ID(N'{TYPE_NAME}') IS NULL" + " " +
                   $"CREATE TYPE {TYPE_NAME} As Table" + " " +
                   $"({COL1_SERVER_ID} int," + " " +
                   $"{COL2_SERVER_SCHEMA} Common._SMALL_," + " " +
                   $"{COL3_TABLE_NAME} Common._SMALL_," + " " +
                   $"{COL4_BRANCH_NO} Common._SMALL_," + " " +
                   $"{COL5_BRANCH_SCHEMA} Common._SMALL_" + " " +
                   $"); ";

sqlQuery += $"SELECT [{ID_FIELD}] " +
                  $"FROM {Settings.VIP_MAPPING_TABLE} " +
                  $"JOIN {STRUCTURED_DATA_TABLE_NAME} ON " +
                  $"{MAP1_SERVER_ID}={COL1_SERVER_ID} " +
                  $" AND {MAP2_SERVER_SCHEMA}={COL2_SERVER_SCHEMA} " +
                  $" AND {MAP3_TABLE_NAME}={COL3_TABLE_NAME} " +
                  $" AND {MAP4_BRANCH_NO}={COL4_BRANCH_NO} " +
                  $" AND {MAP5_BRANCH_SCHEMA}={COL5_BRANCH_SCHEMA} ";// +
                                                                     //$" AND {MAP6_TABLE_ID}={COL6_TABLE_ID}"; 
DataTable dataTable = new DataTable(TYPE_NAME);
dataTable.Columns.Add(COL1_SERVER_ID, typeof(int));
dataTable.Columns.Add(COL2_SERVER_SCHEMA, typeof(string));
dataTable.Columns.Add(COL3_TABLE_NAME, typeof(string));
dataTable.Columns.Add(COL4_BRANCH_NO, typeof(string));
dataTable.Columns.Add(COL5_BRANCH_SCHEMA, typeof(string));
//dataTable.Columns.Add(COL6_TABLE_ID, typeof(int));

for (int i = 0; i < foreignKeyInfoList.Count; i++)
{
    ForeignKeyLookupModelInBranchSide oneKeySet = foreignKeyInfoList[i];
    DataRow row = dataTable.NewRow();
    row[COL1_SERVER_ID] = Int32.Parse(oneKeySet.ServerID);
    row[COL2_SERVER_SCHEMA] = oneKeySet.ServerSchema;
    row[COL3_TABLE_NAME] = oneKeySet.TableName;
    row[COL4_BRANCH_NO] = oneKeySet.BranchNo;
    row[COL5_BRANCH_SCHEMA] = oneKeySet.BranchSchema;
    dataTable.Rows.Add(row);
}

try
{
    using (SqlConnection sqlConnection = new SqlConnection(Settings.connectionString))
    {
        SqlCommand command = new SqlCommand(sqlQuery, sqlConnection);
        SqlParameter p = command.Parameters.Add(STRUCTURED_DATA_TABLE_NAME, SqlDbType.Structured);
        p.Value = dataTable;
        p.TypeName = TYPE_NAME;
        sqlConnection.Open();
        SqlDataReader reader = command.ExecuteReader();
        if (reader.HasRows)
        {
            while (reader.Read())
            {
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    keys.Add(reader.GetInt32(i));
                }
            }
        }
        else
        {
        }
        sqlConnection.Close();
    }
    return new StatusResponseKeysList
    {
        keysList = keys,
        ErrorCode = ErrorCodes.ERROR_CODE_MINUS_019_NULL_OR_EMPTY_INPUT_ARGUMENT
    };
}
catch (Exception e) 
{
    //Exception caught here !!!!!!!: 
    //"Column, parameter, or variable @TVP. : Cannot find data type BranchMappingType."
    if (e.GetType() == typeof(SqlException))
    {
        //this is a db error
        return new StatusResponseKeysList
        {
            ErrorCode = ErrorCodes.ERROR_CODE_MINUS_014_DATABASE_EXCEPTION
        };
    }
    else
    {
        //other types of erros (not a DB-error)
        return new StatusResponseKeysList
        {
            ErrorCode = ErrorCodes.ERROR_CODE_MINUS_001_COMMON_ERROR
        };

    }
}

1 个答案:

答案 0 :(得分:3)

表类型必须存在,然后才能执行使用该类型的参数化查询。原因是在批处理执行之前,数据库引擎首先在tempdb中创建与服务器上定义的表类型相匹配的内部表 。然后,它使用API​​提供的结构化参数值和元数据将TVP值批量插入tempdb中。只有在加载表之后,批处理才会执行,可以在其中使用TVP值。

隐含的含义是,不能创建表类型并不能在使用该类型作为参数的同一批中使用它。

相关问题