我目前正在开发一个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
......等等。
我不确定这是完成此任务的最有效方法。有没有人有他们不介意分享的任何提示或见解?
答案 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上都有教程),请在此处发布任何问题,您应该得到快速答案。