使用外键关系和预处理执行大型插入的最快方法是什么?

时间:2014-01-03 03:38:01

标签: sql-server sql-server-2008-r2 foreign-key-relationship bulkinsert sqlbulkcopy

我需要定期将大型(数十万行)tsv文件导入多个相关的SQL Server 2008 R2表。

输入文件看起来像这样(它实际上更复杂,数据属性不同,但我在这里有类似的):

January_1_Lunch.tsv
+-------+----------+-------------+---------+
| Diner | Beverage | Food        | Dessert |
+-------+----------+-------------+---------+
| Nancy | coffee   | salad_steak | pie     |
| Joe   | milk     | soup_steak  | cake    |
| Pat   | coffee   | soup_tofu   | pie     |
+-------+----------+-------------+---------+

请注意,一列包含字符分隔列表,需要预处理才能将其拆分。

架构高度规范化 - 每条记录都有多个多对多外键关系。这里没什么不寻常的......

Meals
+----+-----------------+
| id |       name      |
+----+-----------------+
|  1 | January_1_Lunch |
+----+-----------------+

Beverages
+----+--------+
| id |  name  |
+----+--------+
|  1 | coffee |
|  2 | milk   |
+----+--------+

Food
+----+-------+
| id | name  |
+----+-------+
|  1 | salad |
|  2 | soup  |
|  3 | steak |
|  4 | tofu  |
+----+-------+

Desserts
+----+------+
| id | name |
+----+------+
|  1 | pie  |
|  2 | cake |
+----+------+

每个输入列最终都指向一个单独的表。

这似乎是一个不必要的复杂架构 - 为什么不只是有一个匹配输入的表?但是考虑到一个小餐馆可能会进入餐厅,只点一杯饮料或甜点,在这种情况下会有很多空行。考虑到这个数据库最终将存储数亿条记录,这似乎是对存储的不良使用。我还希望能够仅为饮料,甜点等生成报告,而且我认为使用单独的表格会更好。

订单在关系表中被跟踪,如下所示:

BeverageOrders
+--------+---------+------------+
| mealId | dinerId | beverageId |
+--------+---------+------------+
|      1 |       1 |          1 |
|      1 |       2 |          2 |
|      1 |       3 |          1 |
+--------+---------+------------+

FoodOrders
+--------+---------+--------+
| mealId | dinerId | foodId |
+--------+---------+--------+
|      1 |       1 |      1 |
|      1 |       1 |      3 |
|      1 |       2 |      2 |
|      1 |       2 |      3 |
|      1 |       3 |      2 |
|      1 |       3 |      4 |
+--------+---------+--------+

DessertOrders
+--------+---------+-----------+
| mealId | dinerId | dessertId |
+--------+---------+-----------+
|      1 |       1 |         1 |
|      1 |       2 |         2 |
|      1 |       3 |         1 |
+--------+---------+-----------+

请注意,Food有更多记录,因为输入包含那些被分成多个记录的讨厌的小列表。这是帮助建立单独表格的另一个原因。


所以问题是,从文件中获取数据的最有效方法是什么?

我考虑过的方法:

  1. 逐行解析tsv文件,按照我的方式执行插入操作。无论是否使用ORM,这似乎很多次访问数据库并且速度非常慢。
  2. 将tsv文件解析为内存中的数据结构或磁盘上与模式对应的多个文件。然后使用SqlBulkCopy导入每个。虽然交易量较少,但由于不得不缓存大量数据或对磁盘执行多次写入,因此它似乎比简单地执行大量插入更加昂贵。
  3. How do I bulk insert two datatables that have an Identity relationshipBest practices for inserting/updating large amount of data in SQL Server 2008,将tsv文件导入临时表,然后合并到模式中,使用数据库函数进行预处理。这似乎是最好的选择,但我认为验证和预处理可以在C#或其他任何地方更有效地完成。
  4. 那里还有其他可能吗?

    架构仍在开发中,所以如果最终成为关键点,我可以修改它。

1 个答案:

答案 0 :(得分:1)

您可以在以下结构的表格中导入您的文件: Diner Beverage Food Dessert ID (身份,主键 NOT CLUSTERED - 用于性能问题)。

在此之后,只需添加以下列:Dinner_IDBeverage_IDDessert_ID并根据您的单独表填充它们(将每个列分组并添加缺失很简单数据以BeveragesDessertsMeals的形式查找表格,之后,使用现有和新添加记录的ID修复导入的表格。“

Food表的情况更复杂,因为能够combine食物,但可以使用相同的技巧:你也可以将数据添加到查找表中,其中,将食物组合存储在附加温度表(具有唯一ID)和单个菜肴上的分离。

当完成分区时,您将有3个临时表:

  1. 包含所有导入数据的表格和所有文字列的ID
  2. 具有不同食物清单(带有ID)的表格
  3. 每种食物组合食物ID的表格
  4. 从上面的表中,您可以根据需要将解析后的值插入到任一结构中。

    在这种情况下,代码端只对DB执行一次插入(批量)操作。所有其他数据操作将在DB中执行。