数据导入问题:我应该使用游标吗?

时间:2011-09-09 23:37:22

标签: sql database database-design data-migration

我目前正在开发一个SQL导入例程,用于将数据从遗留应用程序导入到更现代的强大系统中。该例程只是将平面文件遗留表(存储为.csv文件)中的数据导入到遵循经典订单/订单详细信息模式的SQL Server中。这是两个表的样子:

**LEGACY_TABLE**  
Cust_No  
Item_1_No  
Item_1_Qty  
Item_1_Prc  
Item_2_No  
Item_2_Qty  
Item_2_Prc  
...  
Item_7_No    
Item_7_Qty  
Item_7_Prc  

如您所见,遗留表基本上是一个22列电子表格,用于表示客户,最多分别为7个项目及其数量和购买价格。

新表格如下:

**INVOICE**  
Invoice_No  
Cust_No

**INVOICE_LINE_ITEM**  
Invoice_No  
Item_No  
Item_Qty  
Item_Prc  

我的快速而肮脏的方法是在SQL Server中创建LEGACY_TABLE的副本(让我们称之为LEGACY_TABLE_SQL)。此表将使用已内置到应用程序中的数据库导入从.csv文件填充 从那里,我创建了一个存储过程来实际将LEGACY_TABLE_SQL表中的每个值复制到INVOICE / INVOICE_LINE_ITEM表,以及处理基础逻辑约束(即执行存在测试,检查已打开的发票等)。最后,我创建了一个数据库触发器,在将新数据插入LEGACY_TABLE_SQL表时调用存储过程。

存储过程如下所示:

CREATE PROC IMPORT_PROCEDURE 
@CUST_NO 
@ITEM_NO  
@ITEM_QTY  
@ITEM_PRC  

但是,我实际上使用数据库触发器调用存储过程七次(每个项目一次),而不是一次调用该过程。我只在ITEM_NO为NOT NULL时执行存储过程,以计算.csv文件中的空白项。因此,我的触发器如下所示:

CREATE TRIGGER IMPORT_TRIGGER  
if ITEM_NO_1 IS NOT NULL  
begin  
exec IMPORT_PROCEDURE (CUST_NO,ITEM_NO_1, ITEM_QTY_1, ITEM_PRC_1)  
end  

......等等。

我不确定这是完成此任务的最有效方法。有没有人有他们不介意分享的任何提示或见解?

2 个答案:

答案 0 :(得分:0)

我不确定为什么要添加触发器。你会继续使用LEGACY_TABLE_SQL吗? 如果不是那么这个一次性程序怎么样?它使用Oracle语法,但可以适应大多数数据库

程序移民是

CURSOR all_data是
SELECT invoice_no,cust_no,Item_1_no,Item_1_qty ........
FROM LEGACY_TABLE_SQL;

BEGIN

用于all_data LOOP中的数据 INSERT INTO INVOICE(invoice_no,cust_no)VALUES(data.invoice_no,data.cust_no); 如果Item_1_no不是那么 INSERT INTO INVOICE_LINE_ITEM(invoice_no,Item_1_no,Item_1_qty ....)VALUES(data.invoice_no,data.Item_1_no,data.Item_1_qty ....) 万一; - 为每个项目进一步插入

END LOOP;
COMMIT;
END;

这可以在Oracle中使用BULK_COLLECT进一步优化。 我会为所有项创建INVOICE_LINE_ITEM表,默认值为0.

我也会考虑这些可能性: 发票号码现在和将来真的很独特吗?根据序列添加伪密钥可能是个好主意 null item_no条目有什么重要意义吗?这是否表示延期交货,短货或仅输入错误数据?

编辑:当您建议您将继续使用旧表时,您需要优先考虑您想要的内容。效率和性能是您的首要任务,可维护性,同步事务 例如:
  - 如果性能不是很重要,那么按照您的概述实施   - 如果必须维护,那么您可能希望在编码方面投入更多   - 如果您不需要同步事务,那么您可以向LEGACY_TABLE_SQL添加一个名为处理的列,默认值为0.然后,每天或每小时一次,安排一个作业以获取所有尚未处理的订单。 / p>

答案 1 :(得分:0)

我会将导入过程与任何触发器分开。如果您要从持续运行的外部源中不断将行添加到导入表中,则触发器非常有用。听起来这不是你的情况,因为你将立即导入整个文件。触发器往往隐藏代码,在某些情况下很难处理。

您多久导入一次这些文件?

我的导入过程大多是独立的。它可能使用数据库中的存储过程或表,但我不会使用触发器。一个简单的方法就像下面这样。我在Legacy_Invoices添加了一列(也重命名为更具描述性的内容),以便您可以跟踪项目的导入时间和位置。如有必要,您可以对其进行扩展以跟踪更多信息。

此外,我不知道您是如何跟踪代码中的发票号码的。我在Legacy_Invoices中假设了一个IDENTITY列。这几乎肯定是不够的,因为我假设您也在自己的系统中创建发票(在遗留系统之外)。但是,如果不知道您的发票编号方案,就不可能在那里提供解决方案。

BEGIN TRAN

DECLARE
    @now DATETIME = GETDATE()

UPDATE Legacy_Invoices
SET
    import_datetime = @now
WHERE
    import_status = 'Awaiting Import'

INSERT INTO dbo.Invoices (invoice_no, cust_no)
SELECT DISTINCT invoice_no, cust_no
FROM
    Legacy_Invoices
WHERE
    import_datetime = @now

UPDATE Legacy_Invoices
SET
    import_status = 'Invoice Imported'
WHERE
    import_datetime = @now

INSERT INTO dbo.Invoice_Lines (invoice_no, item_no, item_qty, item_prc)
SELECT
    invoice_no,
    item_no_1,
    item_qty_1,
    item_prc_1
FROM
    Legacy_Invoices LI
WHERE
    import_datetime = @now AND
    import_status = 'Invoice Imported' AND
    item_no_1 IS NOT NULL

UPDATE Legacy_Invoices
SET
    import_status = 'Item 1 Imported'
WHERE
    import_datetime = @now AND
    import_status = 'Invoice Imported'

<Repeat for item_no_2 through 7>

COMMIT TRAN

这是一个警告。虽然游标在SQL中通常是不可取的,并且您希望使用基于集合的处理而不是RBAR(通过痛苦行划分)处理,但数据导入通常是例外。

上述问题是,如果一行失败,则整个导入步骤失败。此外,当您批量导入时,通过业务逻辑运行单个实体(发票加行项目)非常困难。这是SSIS真正闪耀的地方。它非常快(假设您正确设置),即使一次导入一个实体也是如此。然后,您可以在其中放入各种错误处理,以确保导入顺利运行。一个导入行有一个错误的发票号码?没问题,将其标记为错误并继续前进。一行填写了项目#2,但没有项目#1或没有数量的价格?没问题,标记错误并继续。

对于单个导入,我可能会坚持使用上面的代码(当然会添加适当的错误处理),但对于重复的过程,我几乎肯定会使用SSIS。即使每个业务实体都有单独的错误处理,您也可以在几秒或几分钟内导入数百万行。

如果您在运行SSIS时遇到任何问题(在整个网络和Microsoft MSDN上都有教程),请在此处发布任何问题,您应该得到快速答案。