对不同类型的列使用动态逆透视

Using dynamic unpivot with columns with different types

我有一个 table 大约有 100 列,名为 F1、F2、... F100。 我想按行查询数据,如下所示:

F1: someVal1
F2: someVal2
...

我在 SP 中执行所有这些操作,因此,我正在动态生成 sql。 我已成功生成以下 sql:

select CAST(valname as nvarchar(max)), CAST(valvalue as nvarchar(max)) from tbl_name unpivot
(
    valvalue for valname in ([form_id], [F1],[F2],[F3],[F4],[F5],[F6],[F7],[F8],[F9],[F10],[F11],[F12],[F13],[F14],[F15],[F16],[F17],[F18],[F19],[F20],[F21],[F22],[F23],[F24],[F25],[F26],[F27],[F28],[F29],[F30],[F31],[F32],[F33],[F34],[F35],[F36],[F37],[F38],[F39],[F40],[F41],[F42],[F43],[F44],[F45],[F46],[F47],[F48],[F49],[F50],[F51],[F52],[F53],[F54],[F55],[F56],[F57],[F58],[F59],[F60],[F61],[F62],[F63],[F64],[F65],[F66],[F67],[F68],[F69],[F70],[F71],[F72],[F73],[F74],[F75],[F76],[F77],[F78],[F79],[F80],[F81],[F82],[F83],[F84],[F85])
) u

但是在执行这个查询时,我得到了这个异常:

The type of column "F3" conflicts with the type of other columns specified in the UNPIVOT list.

我猜这是因为 F3 是 varchar(100) 而 form_id,F1 和 F2 是 varchar(50)。根据我的理解,我不应该得到这个错误,因为我在 select 语句中将所有结果都转换为 nvarchar(max)。

这个 table 有各种列,如 datetime、smallint 和 int。 此外,此 table 的所有列除一列外都有 SQL_Latin1_General_CP1_CI_AS collaltion

此错误的修复方法是什么?

此解决方案是您必须使用子查询让所有列的类型相同才能具有相同的长度。

尝试 CAST 子查询中的值然后 unpivot 而不是 select

select valname, valvalue 
from (
    SELECT  
        CAST([form_id] as nvarchar(max)) form_id, 
        CAST([F1] as nvarchar(max)) F1,
        CAST([F2] as nvarchar(max)) F2,
        CAST([F3] as nvarchar(max)) F3,
        CAST([F4] as nvarchar(max)) F4,
        ....
    FROM tbl_name 
) t1 unpivot
(
    valvalue for valname in ([form_id], [F1],[F2],[F3],[F4],[F5],[F6],[F7],[F8],[F9],[F10],[F11],[F12],[F13],[F14],[F15],[F16],[F17],[F18],[F19],[F20],[F21],[F22],[F23],[F24],[F25],[F26],[F27],[F28],[F29],[F30],[F31],[F32],[F33],[F34],[F35],[F36],[F37],[F38],[F39],[F40],[F41],[F42],[F43],[F44],[F45],[F46],[F47],[F48],[F49],[F50],[F51],[F52],[F53],[F54],[F55],[F56],[F57],[F58],[F59],[F60],[F61],[F62],[F63],[F64],[F65],[F66],[F67],[F68],[F69],[F70],[F71],[F72],[F73],[F74],[F75],[F76],[F77],[F78],[F79],[F80],[F81],[F82],[F83],[F84],[F85])
) u

以最简单的方式,我会使用 CROSS APPLYVALUES 来完成 unpivot

SELECT * 
    FROM People CROSS APPLY (VALUES 
        (CAST([form_id] as nvarchar(max))),
        (CAST([F1] as nvarchar(max))),
        (CAST([F2] as nvarchar(max))),
        (CAST([F3] as nvarchar(max))),
        (CAST([F4] as nvarchar(max))),
        ....
    ) v (valvalue)

这是关于 CROSS APPLYVALUESunpivot

的示例

我们可以看到 People table.

中有很多不同的类型

我们可以尝试使用castvarchar(max),让列是相同的类型。

CREATE TABLE People
(
  IntVal int, 
  StringVal varchar(50), 
  DateVal date
)

INSERT INTO People VALUES (1, 'Jim', '2017-01-01');
INSERT INTO People VALUES (2, 'Jane', '2017-01-02');
INSERT INTO People VALUES (3, 'Bob', '2017-01-03');

查询 1:

SELECT * 
FROM People CROSS APPLY (VALUES 
    (CAST(IntVal AS VARCHAR(MAX))),
    (CAST(StringVal AS VARCHAR(MAX))),
    (CAST(DateVal AS VARCHAR(MAX)))
) v (valvalue)

Results:

| IntVal | StringVal |    DateVal |   valvalue |
|--------|-----------|------------|------------|
|      1 |       Jim | 2017-01-01 |          1 |
|      1 |       Jim | 2017-01-01 |        Jim |
|      1 |       Jim | 2017-01-01 | 2017-01-01 |
|      2 |      Jane | 2017-01-02 |          2 |
|      2 |      Jane | 2017-01-02 |       Jane |
|      2 |      Jane | 2017-01-02 | 2017-01-02 |
|      3 |       Bob | 2017-01-03 |          3 |
|      3 |       Bob | 2017-01-03 |        Bob |
|      3 |       Bob | 2017-01-03 | 2017-01-03 |

备注

当您使用 unpivot 时需要确保 unpivot 列的日期类型相同。

猫可以通过多种方式为您剥皮,反之亦然。 开个玩笑,D-Shih 建议的是你应该从什么开始,可能会让你回家干。

大多数情况下; 本质上,UNPIVOT 操作是连接来自多行的数据。从 CAST 操作开始是最好的前进方式,因为它使数据类型相同(最好是字符串类型,如 varchar 或 nvarchar),除了具有相同的类型外,为所有 UNPIVOTED 列使用相同的长度也是一个好主意.

其他情况; 如果这仍然不能解决问题,那么您需要更深入地查看并检查 table 上所有列的 ANSI_Padding 设置是打开还是关闭。在后来的 SQL 服务器版本中,默认情况下这主要是打开的,但一些开发人员可能会自定义某些列以将 ANSI_PADDING 设置为关闭。如果您有这样的混合设置,最好将数据移动到另一个 table 并将 ANSI_PADDING 设置为 ON。尝试在 table 上使用相同的 UNPIVOT 查询,它应该可以工作。 检查ANSI_Padding状态

    SELECT name
    ,CASE is_ansi_padded 
    WHEN 1 THEN 'ANSI_Padding_On' 
    ELSE  'ANSI_Padding_Off'
    AS [ANSI_Padding_Check]
                      FROM sys.all_columns
    WHERE object_id = object_id('yourschema.yourtable')

许多情况更适合交叉应用值。这一切都取决于你,骑师选择赛马。

干杯。