【问题标题】:Horizontal data into Vertical水平数据转换成垂直
【发布时间】:2012-10-17 20:18:29
【问题描述】:

我有水平表(Table1),我需要将其转换为(Table2)

表 1:

CEO     SALESMAN    PRODUCT(1)  PRODUCT(2)  PRODUCT(3)  PRODUCT(4)  PRODUCT(5)  ... PRODUCT(N)
------  ----------  ----------  ----------  ----------  ----------  ----------      ----------
MIKE    ANDERSON    76787,00    19388,00    0,00        2723,00     217,00          6581,00
JOHN    ANGELA      0,00        0,00        73088,00    0,00        0,00            0,00
JACK    JEFF        24716,00    0,00        2995,00     0,00        0,00            0,00
STUART  MICHAEL     0,00        23338,00    42656,00    0,00        0,00            0,00

表 2:

CEO     SALESMAN    PRODUCTS    VALUE
------- ----------- ----------  --------
MIKE    ANDERSON    PRODUCT(1)  76787,00
JOHN    ANGELA      PRODUCT(1)  0,00
JACK    JEFF        PRODUCT(1)  24716,00
STUART  MICHAEL     PRODUCT(1)  0,00
MIKE    ANDERSON    PRODUCT(2)  19388,00
JOHN    ANGELA      PRODUCT(2)  0,00
JACK    JEFF        PRODUCT(2)  0,00
STUART  MICHAEL     PRODUCT(2)  23338,00
MIKE    ANDERSON    PRODUCT(3)  0,00
JOHN    ANGELA      PRODUCT(3)  73088,00
JACK    JEFF        PRODUCT(3)  2995,00
STUART  MICHAEL     PRODUCT(3)  42656,00
MIKE    ANDERSON    PRODUCT(4)  2723,00
JOHN    ANGELA      PRODUCT(4)  0,00
JACK    JEFF        PRODUCT(4)  0,00
STUART  MICHAEL     PRODUCT(4)  0,00
MIKE    ANDERSON    PRODUCT(5)  217,00
JOHN    ANGELA      PRODUCT(5)  0,00
JACK    JEFF        PRODUCT(5)  0,00
STUART  MICHAEL     PRODUCT(5)  0,00
MIKE    ANDERSON    ...     ...
JOHN    ANGELA      ...     ...
JACK    JEFF        ...     ...
STUART  MICHAEL     ...     ...
MIKE    ANDERSON    PRODUCT(N)  6581,00
JOHN    ANGELA      PRODUCT(N)  0,00
JACK    JEFF        PRODUCT(N)  0,00
STUART  MICHAEL     PRODUCT(N)  0,00

到目前为止,我尝试的是将 Table1 批量插入到临时表中,然后处理这些数据,直到我得到我想要的。问题是 PRODUCT() 列中的 N 是可变的,因此我无法创建具有固定列的临时表,我需要一个动态查询,以某种方式可以读取产品列的数量并使用它。

批量插入

BULK INSERT #temp
FROM '\\path\file.csv'
WITH
(
FIRSTROW = 1,
FIELDTERMINATOR= ';',
ROWTERMINATOR = '\n',
CODEPAGE='RAW'
);

临时表:

CREATE TABLE    #temp(
col1    varchar(100)    null,
col2    varchar(100)    null,
col3    varchar(100)    null,
col4    varchar(100)    null,
col5    varchar(100)    null,
...
col397  varchar(100)    null,
col398  varchar(100)    null,
col399  varchar(100)    null,
col400  varchar(100)    null
)

当我运行 BULK INSERT 时出现此错误:

Msg 4832, Level 16, State 1, Line 1
Bulk load: An unexpected end of file was encountered in the data file.
Msg 7399, Level 16, State 1, Line 1
The OLE DB provider "BULK" for linked server "(null)" reported an error. The provider did not give any information about the error.
Msg 7330, Level 16, State 2, Line 1
Cannot fetch a row from OLE DB provider "BULK" for linked server "(null)".

发生这种情况是因为我创建了一个固定 400 列的临时表,并且有 127 个产品列。

我试图避免这样的事情:

DECLARE @NUM_ROWS INT

SET     @NUM_ROWS = 123

IF @NUM_ROWS = 1
BEGIN
CREATE TABLE    #temp(
col1    varchar(100)    null
)
END

IF @NUM_ROWS = 2
BEGIN
CREATE TABLE    #temp(
col1    varchar(100)    null,
col2    varchar(100)    null,
)
END

...

IF @NUM_ROWS = 400
BEGIN
CREATE TABLE    #temp(
col1    varchar(100)    null,
col2    varchar(100)    null,
...
col4    varchar(100)    null,
)
END

BULK INSERT #temp
FROM '\\path\file.csv'
WITH
(
FIRSTROW = 1,
FIELDTERMINATOR= ';',
ROWTERMINATOR = '\n',
CODEPAGE='RAW'
);

@NUM_ROWS 将是我将创建临时表的列数。

有谁知道我可以将此 .csv 文件动态导入 SQL Server 的方法吗?动态我的意思是创建一个临时表,其中包含我批量插入的 PRODUCTS 列数。或者避免我在上面发布的错误的解决方法。

【问题讨论】:

  • 你可以在加载 csv 文件之前对其进行任何手动操作吗?还是这个过程需要 100% 自动化?
  • 当然,我之前可以操作 .csv 文件。实际上,最好是先操纵它。我搜索了有关操作它的信息,并发现了在粘贴值时进行转置,但它没有帮助。
  • 这是一次性的任务,还是需要重复?
  • @podiluska 我需要重复一遍。我每个月都会收到大约 16 个这样的 .csv 文件。

标签: sql-server sql-server-2008 import-from-excel


【解决方案1】:

这是一种有点手动的方法:

  1. 用 Excel 打开 .csv 文件
  2. 将 400 个列名添加到第一行。当然,它们不必是字段的实际名称。只是占位符。这样,当您重新保存文件时,该文件将有 400 列。 (我称之为手动方法,但您可以使用 VBA 脚本自动执行此操作)

从这里开始,全是 SQL:

  1. 使用标准批量插入,将其导入到具有 400 列的临时表中。
  2. 使用以下方式将数据插入 table2:
Insert into Table2
Select CEO, Salesmen, 1 as ProductNum, [Product(1)] from Table1 where [Product(1)] is not null
union
Select CEO, Salesmen, 2 as ProductNum, [Product(2)] from Table1 where [Product(2)] is not null
...
union
Select CEO, Salesmen, 400 as ProductNum, [Product(400)] from Table1 where [Product(400)] is not null

当然,这 400 行会很难看,但您可以轻松阅读并从 1 数到 400。

【讨论】:

  • 这是一个简单的好主意,这样我就可以使用一个固定的 400 列临时表并适用于任何文件。我以后要做的就是从临时表中删除TOP 1
  • 由于产品名称每次都不相同,我将不得不以其他方式执行 SQL 部分。产品列不会超过 200 列,因此我将保留 200 列来放置产品描述并执行以下操作:首先我插入一个带有 ID 的列,然后 UPDATE #temp SET column204 = select column4 where ID = 2, UPDATE #temp SET column205 = select column5 where ID = 2, UPDATE #temp SET column206 = select column6 where ID = 2... 依此类推,这样我就知道column4与column205相关,5到205,6到206,等等。最后我会按照你的建议去做。
  • Insert into Table2 Select CEO, Salesmen, column4 as product_value, column204 as product_description from #temp union Select CEO, Salesmen, column5, column205 from #temp ... union Select CEO, Salesmen, column200, column400 from #temp
  • 关于您的第一条评论,批量插入有一个可选参数 FirstRow,您可以将其设置为 2。无需从临时表中删除前 1。
  • 我喜欢你的想法。听起来您正试图通过将产品名称存储在自己的列中来从列名中抽象出产品名称。祝你好运。
【解决方案2】:

如果您将 CSV 打开到 Excel 中,然后再次将其另存为 CSV,那么它会将其格式化为具有固定数量的列。

您可以使用循环和动态 SQL 创建临时表

declare @sql nvarchar(max), @numcols int = 8

select @sql = ''
declare @i int = 1

select @sql = @sql + ', c' + convert(varchar(5), number) + ' varchar(10) ' 
from master..spt_values 
where type='p' and number between 1 and @numcols

select @sql = 'create table ##temp (' + substring(@sql, 3, len(@sql)) + ')'
select @sql

exec sp_executeSQl @sql

BULK INSERT ##temp  
FROM '\\path\file.csv'  
WITH  
(  
FIRSTROW = 1,  
FIELDTERMINATOR= ';',  
ROWTERMINATOR = '\n',  
CODEPAGE='RAW'  
);  

从那里您只需应用 Unpivot 将表格从列转换为行。

【讨论】:

  • 运行上面的代码时,我收到以下错误:(1 row(s) affected) Msg 208, Level 16, State 82, Line 15 Invalid object name '#temp'. 关于使用 Unpivot,我将不得不 SELECT 产品列的静态列表,这无济于事,因为产品可能变化。
  • 我的错。 temp前应该有两个##。如果您的产品不同,那么您将需要通过动态sql构建unpivot。
猜你喜欢
  • 2022-11-16
  • 1970-01-01
  • 2018-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多