在SQL Server数据库中导入可变时间序列数据

我已经在堆栈溢出和其他地方寻找一个解决这个问题的办法,但找不到处理卷的例子。 如果我错过了在其他地方发布的解决scheme,如果有人能指引我正确的方向,我将不胜感激。

我正在尝试从45个不同的Excel工作表导入时间序列数据(每个Excel工作簿约5个)。 每个工作表都包含商品价格系列,涵盖了每种商品的几年每日价格。

原始的Excel数据每天有一行可能存在价格,每个商品合约一列,通常是月度期货合约。 因此,每个合同的积分至less是30(而不是更多),而整个表格有几千行和100多列。 我能够构build一个读取数据并使用unpivot的SSIS包,将matrix转换为具有以下列的行logging:

Date, Price, Contract 

但问题是,在unpivot变换我不得不手动指定每个转换的input列的目标列。 所以有45个工作表,每个工作表包含超过100列(甚至几百个)的合同,我最终会在接下来的几天里手动对这些转换进行“硬编码”。最后,这不是灵活/正如我所希望的那样可用。

附加的原始数据示例(整个Cocoa工作表包含9724行和195列)

原始数据示例

下面是如何configuration另一种商品的unpivot。 “目标列”必须逐行手动填写。

在这里输入图像描述

我希望我刚刚错过了在unpivotconfiguration中的正确步骤,使这些列dynamic。 理想情况下,SSIS解决scheme可以在以后再次使用同样格式化的Excel工作簿。 不需要在服务器上运行它,因为它不是经常发生的事情,而是每年最多一次或两次。 所以我可以很容易地从VS内部手动启动它。

我试图帮助学术研究人员,否则将花费大量时间在Excel中手动清理和分析数据。

首先,您需要devise和创build将要接收数据的表格,并尝试使用一些手动数据input来检查数据模型。

确保每个电子表格都有足够的标题信息来知道如何处理行。

完成后,我会将图纸保存为带有制表符分隔符的文本文件。

接下来我会用Perl编写一个加载程序。 它首先读取标题行并确定将行插入数据库的规则。 然后每一行被转换成一个插入数据库。

以下是我自己发票加载计划的一个例子(所有权利):

 if ($first) { $obj->_hdr2keys(0); # convert spreadhseet header into a lookup my $hdr = $obj->_copystruct($obj->{ar}[0]); my @Hhdr = ('invoice header id'); my @Hcols = ('invhid'); my @Htypes = ('serial'); my @Dhdr = ('invoice detail id'); my @Dcols = ('invdid','invhid'); my @Dtypes = ('serial','integer'); for (my $col=0; $col <= $#{$hdr}; $col++) { my $colname = lc($obj->_pomp($hdr->[$col])); if ($colname eq 'invoicenumber') { push @Hhdr, $hdr->[$col]; push @Hcols, $colname; push @Htypes, 'char(32)'; } elsif ($colname eq 'buysell') { push @Hhdr, $hdr->[$col]; push @Hcols, $colname; push @Htypes, 'boolean'; } elsif ($colname eq 'suppliercustomer') { push @Hhdr, $hdr->[$col]; push @Hcols, $colname; push @Htypes, 'char(64)'; } elsif ($colname eq 'date') { push @Hhdr, 'Transaction Date'; push @Hcols, 'transactiondate'; push @Htypes, 'date'; } elsif ($colname eq 'article') { push @Dhdr, 'Article id'; push @Dcols, 'artid'; push @Dtypes, 'integer'; push @Dhdr, 'Article Description'; push @Dcols, 'description'; push @Dtypes, 'char(64)'; } elsif ($colname eq 'qty') { push @Dhdr, $hdr->[$col]; push @Dcols, $colname; push @Dtypes, 'integer'; } elsif ($colname eq 'priceexclbtw') { push @Dhdr, $hdr->[$col]; push @Dcols, $colname; push @Dtypes, 'double precision'; } elsif ($colname eq 'btw') { push @Dhdr, $hdr->[$col]; push @Dcols, $colname; push @Dtypes, 'real'; } } $obj->_getset('INVHar', ['invoiceheader', ['PK','invhid'], ['__COLUMNS__'], \@Hcols, \@Htypes, \@Hhdr ] ); $obj->_getset('INVDar', ['invoicedetail', ['PK','invdid'], ['FK','invhid','invoiceheader','invhid'], ['FK','artid','article','artid'], ['__COLUMNS__'], \@Dcols, \@Dtypes, \@Dhdr ] ); } $first = 0; SALESROW: for (my $i=1; $i <= $#{$obj->{ar}}; $i++) { my @Hrow = (''); my @Drow = (''); my $date = $obj->_selectar('', $i, 'Date'); $date =~ s/\-/\//g; if ($date) { $obj->_validCSV('date', $date) or die "CSV format error date |$date| in file $file"; } my $invtotal = ($obj->_selectar('', $i, 'Invoice Total incl. BTW')); my $article = $obj->_selectar('', $i, 'Article'); $date or $article or next SALESROW; if ($date) { push @Hrow, $obj->_selectar('', $i, 'Invoice Number'); my $buysell = $obj->_selectar('', $i, 'Buy/Sell'); push @Hrow, ($buysell eq 'S') ? 1 : 0; push @Hrow, $obj->_selectar('', $i, 'Supplier/Customer'); push @Hrow, $date; push @{$obj->_getset('INVHar')}, \@Hrow; $invhid++; } push @Drow, $invhid; if ($article eq 'E0154') { push @Drow, 1; } elsif ($article eq 'C0154') { push @Drow, 2; } elsif ($article eq 'C0500') { push @Drow, 3; } elsif ($article eq 'C2000') { push @Drow, 4; } elsif ($article eq 'C5000') { push @Drow, 5; } else { die "unrecognised sales article $article\n" . Dumper($obj->{ar}[$i]); } push @Drow, undef; # description is in article table push @Drow, $obj->_selectar('', $i, 'Qty.'); push @Drow, $obj->_selectar('', $i, 'Price excl. BTW'); push @Drow, $obj->_selectar('', $i, 'BTW %'); push @{$obj->_getset('INVDar')}, \@Drow; } 

在产品表已经从另一电子表格加载之后,这将为发票创build标题和明细logging。

在上面的例子中,创build了两个数组数组,INVHar和INVDar。 准备就绪后,调用例程将它们加载到数据库中,如下所示。 在下一个代码示例中,将创build表以及行,还将更新metadb以加载将来表以及pipe理现有表的外键。 在前面的代码片段中创build的数组包含创build表格和插入行所需的所有信息。 还有一个简单的例程_DBdatacnv,用于在电子表格中的格式和数据库中所需的格式之间进行转换。 例如,电子表格中有货币符号需要在插入前剥离。

 sub _arr2db { my ($obj) = @_; my $ar = $obj->_copystruct($obj->_getset('ar')); my $dbh = $obj->_getset('CDBh'); my $mdbh = $obj->_getset('MDBh'); my $table = shift @$ar; $mdbh->{AutoCommit} = 0; $dbh->{AutoCommit} = 0; my @tables = $mdbh->selectrow_array( "SELECT id FROM mtables WHERE name = \'$table\'" ); my $id = $tables[0] || ''; if ($id) { $mdbh->do("DELETE FROM mcolumns where tblid=$id"); $mdbh->do("DELETE FROM mtables where id=$id"); } # process constraints my %constraint; while ($#{$ar} >= 0 and $ar->[0][0] ne '__COLUMNS__') { my $cts = shift @$ar; my $type = shift @$cts; if ($type eq 'PK') { my $pk = shift @$cts; $constraint{$pk} ||= ''; $constraint{$pk} .= ' PRIMARY KEY'; @$cts and die "unsupported compound key for $table"; } elsif ($type eq 'FK') { my ($col, $ft, $fk) = @$cts; $ft && $fk or die "incomplete FK declaration in CSV for $table"; $constraint{$col} ||= ''; $constraint{$col} .= sprintf( ' REFERENCES %s(%s)', $ft, $fk ); } elsif ($type eq 'UNIQUE') { while (my $uk = shift @$cts) { $constraint{$uk} ||= ''; $constraint{$uk} .= ' UNIQUE'; } } elsif ($type eq 'NOT NULL') { while (my $nk = shift @$cts) { $constraint{$nk} ||= ''; $constraint{$nk} .= ' NOT NULL'; } } else { die "unrecognised constraint |$type| for table $table"; } } shift @$ar; unless ($mdbh->do("INSERT INTO mtables (name) values (\'$table\')")) { warn $mdbh->errstr . ": mtables"; $mdbh->rollback; die; } @tables = $mdbh->selectrow_array( "SELECT id FROM mtables WHERE name = \'$table\'" ); $id = shift @tables; $dbh->do("DROP TABLE IF EXISTS $table CASCADE") or die $dbh->errstr; my $create = "CREATE TABLE $table\n"; my $cols = shift @$ar; my $types = shift @$ar; my $desc = shift @$ar; my $first = 1; my $last = 0; for (my $i=0; $i<=$#{$cols}; $i++) { $last = 1; if ($first) { $first = 0; $create .= "( " } else { $create .= ",\n"; } $create .= $cols->[$i] . ' ' . $obj->_DBcnvtype($types->[$i]); $constraint{$cols->[$i]} and $create .= ' ' . $constraint{$cols->[$i]}; unless ($mdbh->do("INSERT INTO mcolumns (tblid,name,type,description) values ($id,\'$cols->[$i]\',\'$types->[$i]\',\'$desc->[$i]\')")) { warn $mdbh->errstr; $mdbh->rollback; die; } } $last and $create .= ')'; unless ($dbh->do($create)) { warn $dbh->errstr; $dbh->rollback; die; } my $count = 0; while (my $row = shift @$ar) { $count++; my $insert = "INSERT INTO $table ("; my $values = 'VALUES ('; my $first = 1; for (my $i=0; $i<=$#{$cols}; $i++) { my $colname = $cols->[$i]; unless (defined($constraint{$colname}) and $constraint{$colname} =~ /PRIMARY KEY/) { if ($first) { $first = 0; } else { $insert .= ', '; $values .= ', '; } $insert .= $colname; my $val = $obj->_DBdatacnv('CSV', 'DB', $types->[$i],$row->[$i]); if ($val eq '%ABORT') { $mdbh->rollback; die; } $values .= $val; } } $insert .= ')' . $values . ')'; unless ($dbh->do($insert)) { warn $dbh->errstr; warn $insert; $mdbh->rollback; die; } } NOINSERT: $mdbh->commit; $dbh->commit; # warn "inserted $count rows into $table"; } 

更新:好的我将添加从CSV转换为arrays的通用例程为_arr2dbarrays准备好以上我为系统的所有其他情况:电子表格首先增加了PK FK和其他约束,其次是一个列名头数据库中,一行数据库types(名义上,实际上在_DBcnvdatatype中被关注),然后一行标签放在元数据库中,最后是一个在插入数据行之前的令牌列。

 sub _csv2arr { my ($obj, $csv ) = @_; my $ar = []; my $delim = $obj->_getset('csvdelim') || '\,'; my $basename = basename($csv); $basename =~ s/\.txt$//; $ar = [$basename]; open my $fh, $csv or die "$!: $csv"; while (<$fh>) { chomp; my $sa = []; @$sa = split /$delim/; push @$ar, $sa; } close $fh; $obj->{ar} = $ar; } 

我会做这一系列的嵌套循环:

循环1遍历文件夹中的所有文件。 传递文件名到下一个循环

循环2打开文件,遍历表单

循环3在表格X中循环遍历列(A)> 1

循环4 – 循环遍历行:

阅读表格X,行B,

从( 行B,列1 )获取值作为date,( 行1,列A )作为产品。 ( 行B,列A )作为价格 – 写入目的地。

结束循环4(可选,在列的最后logging一些关于行数的元数据)

循环结束3(可选,logging一些关于表格中列数的元数据)

循环结束2(可选,logging关于文件X中的页数的一些元数据)

循环结束1(强烈build议 – logging一些关于文件X的元数据和页数/行数/列数 – 您可以稍后testing样本,以确保您的信心)

您可能需要修改其中一个文件的副本,以便testing问题。

  • 表空

  • 无效数据

  • 缺less标题

  • 文字而不是价格

这将增加更多的信心,并会缩短发现新边缘案例时所需的返工。

最终输出表应该是3列中的非规格化数据:

 Date, Product, Price 

编辑这里是一个链接,显示如何dynamic循环通过Excel电子表格(和工作表)的列,以便您可以使用此过程将数据untivot到规范化的forms

通过SSIS中的Excel列循环