合并3列以组成唯一数据库列的最佳方法?触发?

时间:2019-01-22 23:27:03

标签: sql-server database ef-core-2.1

我有一张桌子,上面看起来像这样。

ItemsTbl
id -PK
BrandId- int (FK)
LocationId- int (FK)
Field3 - nvarchar(Max) Json string containing a jsonKey called itemNumber

现在,我正在构建一个导入器(在excel文件中导入),一次将批量导入1000个项目。但是在插入它们之前,我必须检查它们是否存在于数据库中。

如果是,则更新,如果不是新记录。

理想的世界,我要么希望客户在其excel文件中有一个唯一编号的字段(通过数据库中的pk或其他东西),然后将其存储在我的数据库中。如果它与数据库中的匹配,则更新,否则为新的。

业务部门告诉我,这是不可能的,因为他们认为90%的客户群将无法弄清楚如何为我们提供带有唯一列的文件以用作标识符。

我也无法再次使用唯一编号重新生成文件,因为我被告知大多数人每次决定导入时都会重新列出列表。

所以新的想法是在数据库上有一个新列,它将合并BrandId + LocationId + itemNumber,然后每当文件通过时,将excel文件中的这3个字段都加入并进行检查。

我担心此字段会变得不同步,就像他们手动进入我们的系统并更改该字段必须更改的那三个字段中的任何一个一样。

我想触发器是最好的方法,但是我不确定是否可以在包含json的字段上使用触发器。

我正在使用EF Core,Sql Server 2017。

我不尝试做这样的事情的原因

select * from ItemsTbl where BrandId = 1 AND LocationId = 1 And JSON_VALUE('Field3',$.itemNumber) = 12345

是因为就像我说的那样,我一次要获取1000个项目,所以我不想进行1000个单独的查询。我宁愿使用in子句进行1次查询,然后循环遍历结果并进行更新。

1 个答案:

答案 0 :(得分:1)

我建议您使用标准ADO.NET库执行此操作。

创建一个过程,该过程将“表值参数”作为输入,并在该参数内传递批量数据。之后,您可以使用MERGE函数通过一个调用进行更新和插入(UPSERT)操作。

/* Create a table type. */  
CREATE TYPE SourceTableType AS TABLE   
( BrandId INT  
, LocationId INT
, ItemNumber INT
, ...
);  
GO 


CREATE PROCEDURE dbo.usp_InsertTvp  
@Source SourceTableType READONLY  
    AS        
    MERGE INTO Table1 AS Target  
    USING @Source As Source 
      ON Target.BrandId = Source.BrandId and
         Target.LocationId = Source.LocationId and
         Target.ItemNumber = Source.ItemNumber  
    WHEN MATCHED THEN  
    UPDATE SET OtherParam = Source.OtherParam  
    WHEN NOT MATCHED BY TARGET THEN  
    INSERT (BrandId, LocationId, ItemNumber, OtherParam) VALUES (BrandId, LocationId, ItemNumber, OtherParam) ; 

在.Net端,您需要使用

using (connection)  
{  
  // Create a DataTable with the modified rows.  
  DataTable myTable = ...;  

  // Define the INSERT-SELECT statement.  
  string sqlInsert = "dbo.usp_InsertTvp"  

  // Configure the command and parameter.  
  SqlCommand mergeCommand = new SqlCommand(sqlInsert, connection);  
  mergeCommand.CommandType = CommandType.StoredProcedure;
  SqlParameter tvpParam = mergeCommand.Parameters.AddWithValue("@Source", myTable);  
  tvpParam.SqlDbType = SqlDbType.Structured;  
  tvpParam.TypeName = "dbo.SourceTableType";  

  // Execute the command.  
  insertCommand.ExecuteNonQuery();  
}  

PS:该代码未经测试,可能存在语法问题。


合并语法

MERGE  [ INTO ] <target_table> [ AS ] table_alias
USING <table_source>   
  ON <merge_search_condition>  
WHEN MATCHED [ AND <clause_search_condition> ]  
    THEN <merge_matched> ] [ ...n ]  
[ WHEN NOT MATCHED [ BY TARGET ] [ AND <clause_search_condition> ]  
    THEN <merge_not_matched> ]  

其中

merge_matched>::=  
{ UPDATE SET <set_clause> | DELETE }  

<merge_not_matched>::=  
{  
    INSERT [ ( column_list ) ]   
        { VALUES ( values_list )  
        | DEFAULT VALUES }  
}  

Reference