【问题标题】:What's the fastest way to perform large inserts with foreign key relationships and preprocessing?使用外键关系和预处理执行大型插入的最快方法是什么?
【发布时间】:2014-01-03 03:38:01
【问题描述】:

我需要定期将大型(数十万行)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 文件导入临时表,然后合并到模式中,使用 DB 函数进行预处理。这似乎是最好的选择,但我认为验证和预处理可以在 C# 或其他任何东西中更有效地完成。

还有其他可能性吗?

该架构仍在开发中,因此如果最终成为症结,我可以对其进行修改。

【问题讨论】:

  • 4.使用 SSIS........
  • 我建议您重新考虑将饮料、餐点和甜点分开,并考虑为这些项目设置一个单独的桌子,并用代码表示“类型”或“类别”。为什么?考虑一下添加“starters”或“side orders”的业务变更,添加新类别比添加全新表要容易得多。不要过度担心空间效率低下;获得一个好的模型更为重要。
  • @MitchWheat - 我感觉 SQL Server 集成服务可能很有用,但对它知之甚少。你能再具体一点吗?我们在这里谈论 ETL 吗?
  • @Rikalous - 如果我们谈论的是数以亿计的充满空值的行,我们在哪里划定界限来确定什么是过度担忧和什么是谨慎的?

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


【解决方案1】:

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

在此之后只需添加以下列:Dinner_IDBeverage_IDDessert_ID 并根据您的单独表填充它们(将每个列分组并将缺少的数据添加到查找表中很简单@ 987654324@、DessertsMeals,然后用现有记录和新添加记录的 ID 修复导入的表。

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

parcing 完成后,您将拥有 3 个临时表:

  1. 包含所有导入数据和所有文本列 ID 的表格
  2. 带有不同食物清单(带有 ID)的表格
  3. 每个食物组合的食物 ID 表

从上面的表格中,您可以根据需要将解析的值插入到任一结构中。

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

【讨论】:

  • 对于Food 表,将在哪里执行字符串处理以分离单个菜肴? SQL Server 可以有效地处理这个问题吗?
  • 可以编写简单的解析器,它将接收字符串和分隔符,并通过提供的分隔符将字符串分隔到表格中。
  • 这是很久以前的事了,但是在尝试使用 SSIS 进行几次尝试后,我们发现它对 100000 多行的查找表现不佳。批量加载到临时表中,为外键添加列,然后将这些行插入到目标表中。
猜你喜欢
  • 1970-01-01
  • 2020-03-16
  • 2013-07-01
  • 2020-12-22
  • 1970-01-01
  • 1970-01-01
  • 2011-05-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多