6

我知道你有很多关于这个的话题。但是,我还没有找到满足我需求的。我需要(按需)将选择深层表数据透视到一个宽输出表。这里的问题是我不能将聚合与 Pivot 一起使用,因为它会吃掉输出中所需的响应。我已经想出了一个解决方案,但我认为这不是最好的,因为它需要无数个左连接才能工作。我已将所有尝试和注释包括在内,如下所示:

-- Sql Server 2008 数据库。
-- 深层表结构(不受修改)包含名称/值对,用户 ID 为
——外键。在许多情况下,用户为
-- itemName 比如如果问他们的种族,可以回答白人+西班牙人等。每个回答都被存储
- 作为单独的记录 - 目前无法更改。

-- 目标:将深层数据转向宽数据,同时压缩结果
-  安定下来。每个 userId 的所有项目的帐户,并复制
-- 适用的列值(而不是显示空值)

-- 用于存储单个和多个响应的一些数据的示例表
将@testTable 声明为表(userId int,itemName varchar(50),itemValue varchar(255))

插入@testTable
选择 1,'q01','1-q01 答案'
UNION SELECT 1, 'q02', '1-q02 答案'
UNION SELECT 1, 'q03', '1-q03 答案 1'
UNION SELECT 1, 'q03', '1-q03 答案 2'
UNION SELECT 1, 'q03', '1-q03 答案 3'
UNION SELECT 1, 'q04', '1-q04 ​​答案'
UNION SELECT 1, 'q05', '1-q05 答案'
UNION SELECT 2, 'q01', '2-q01 答案'
UNION SELECT 2, 'q02', '2-q02 答案'
UNION SELECT 2, 'q03', '2-q03 答案 1'
UNION SELECT 2, 'q03', '2-q03 答案 2'
UNION SELECT 2, 'q04', '2-q04 ​​答案'
UNION SELECT 2, 'q05', '2-q05 答案'

选择“原始数据”
选择 * 从 @TestTable

SELECT '使用 Pivot - 显示每个 itemName 的 itemValue 的汇总结果 - 吃掉其他人'
; 数据为 (
    选择
        [用户身份]
        , [项目名称]
        , [项目值]
    从
        @testTable
)
选择
    [用户身份]
    , [q02]
    , [q03]
    , [q05]
从
    数据
枢
(
    MIN(itemValue) -- 聚合函数吃掉需要的值。
    FOR itemName in ([q02], [q03], [q05])
) 作为数据透视表


SELECT '聚合与分组 - 导致空值'
选择
    不同的用户 ID
    ,[q02] = Max(CASE WHEN itemName = 'q02' THEN itemValue END)
    ,[q03] = Max(CASE WHEN itemName = 'q03' THEN itemValue END)
    ,[q05] = Max(CASE WHEN itemName = 'q05' THEN itemValue END)
从
    @testTable
在哪里
    itemName in ('q02', 'q03', 'q05') -- 让它变得更快
通过...分组
    userId -- 如果仅通过 userId,它只给出 1 行 PERIOD = BAD!!
    , [项目名称]
    , [项目值]


SELECT '多个左连接 - 工作正常但如果旋转 175 列左右则不好'
; 数据为 (
    选择
        用户身份
        ,[项目名称]
        ,[项目值]
    从
        @testTable
    在哪里
        itemName in ('q02', 'q03', 'q05') -- 让它变得更快
)
选择
    不同的 s1.userId
    ,[q02] = s2.[itemValue]
    ,[q03] = s3.[itemValue]
    ,[q05] = s5.[itemValue]
从
    数据 s1
    左连接数据 s2
        ON s2.userId = s1.userId
            AND s2.[itemName] = 'q02'
    左连接数据 s3
        ON s3.userId = s1.userId
            AND s3.[itemName] = 'q03'
    左连接数据 s5
        ON s5.userId = s1.userId
            AND s5.[itemName] = 'q05'

所以底部查询是唯一一个(到目前为止)可以做我需要做的事情,但是当我使用实际项目名称进行透视时,LEFT JOIN 会失控并导致性能问题。任何建议表示赞赏。

4

3 回答 3

3

I think you'll have to stick with joins, because joins are exactly the way of producing results like the one you are after. The purpose of a join is to combine row sets together (on a condition or without any), and your target output is nothing else than a combination of subsets of rows.

However, if the majority of questions always have single responses, you could substantially reduce the number of necessary joins. The idea is to join only multiple-response groups as separate row sets. As for the single-response items, they are joined only as part of the entire dataset of target items.

An example should better illustrate what I might poorly describe verbally. Assuming there are two potentially multiple-response groups in the source data, 'q03' and 'q06' (actually, here's the source table:

DECLARE @testTable AS TABLE(
  userId int,
  itemName varchar(50),
  itemValue varchar(255)
);

INSERT INTO @testTable
SELECT 1, 'q01', '1-q01 Answer'
UNION SELECT 1, 'q02', '1-q02 Answer'
UNION SELECT 1, 'q03', '1-q03 Answer 1'
UNION SELECT 1, 'q03', '1-q03 Answer 2'
UNION SELECT 1, 'q03', '1-q03 Answer 3'
UNION SELECT 1, 'q04', '1-q04 Answer'
UNION SELECT 1, 'q05', '1-q05 Answer'
UNION SELECT 1, 'q06', '1-q06 Answer 1'
UNION SELECT 1, 'q06', '1-q06 Answer 2'
UNION SELECT 1, 'q06', '1-q06 Answer 3'
UNION SELECT 2, 'q01', '2-q01 Answer'
UNION SELECT 2, 'q02', '2-q02 Answer'
UNION SELECT 2, 'q03', '2-q03 Answer 1'
UNION SELECT 2, 'q03', '2-q03 Answer 2'
UNION SELECT 2, 'q04', '2-q04 Answer'
UNION SELECT 2, 'q05', '2-q05 Answer'
UNION SELECT 2, 'q06', '2-q06 Answer 1'
UNION SELECT 2, 'q06', '2-q06 Answer 2'
;

which is same as the table in the original post, but with added 'q06' items), the resulting script could be like this:

WITH ranked AS (
  SELECT
    *,
    rn = ROW_NUMBER() OVER (PARTITION BY userId, itemName ORDER BY itemValue)
  FROM @testTable
),
multiplied AS (
  SELECT
    r.userId,
    r.itemName,
    r.itemValue,
    rn03 = r03.rn,
    rn06 = r06.rn
  FROM ranked r03
    INNER JOIN ranked r06 ON r03.userId = r06.userId AND r06.itemName = 'q06'
    INNER JOIN ranked r ON r03.userId = r.userId AND (
      r.itemName = 'q03' AND r.rn = r03.rn OR
      r.itemName = 'q06' AND r.rn = r06.rn OR
      r.itemName NOT IN ('q03', 'q06')
    )
  WHERE r03.itemName = 'q03'
    AND r.itemName IN ('q02', 'q03', 'q05', 'q06')
)
SELECT userId, rn03, rn06, q02, q03, q05, q06
FROM multiplied
PIVOT (
  MIN(itemValue)  
  FOR itemName in (q02, q03, q05, q06)
) AS PivotTable
于 2011-10-25T17:43:46.780 回答
3
; WITH SRData AS (
    SELECT  -- Only query single response items in this block
        [userId]
        , [q01]
        , [q02]
        , [q04]
        , [q05]
    FROM
        @testTable
    PIVOT
    (
        MIN(itemValue) 
        FOR itemName in ([q01], [q02], [q04], [q05])
    ) AS PivotTable
)
SELECT
    sr.[userId]
    , sr.[q01]
    , sr.[q02]  
    , [q03] = mr03.[itemValue]
    , sr.[q04]      
    , sr.[q05]      
    , [q06] = mr06.[itemValue]
FROM
    SRData sr
    LEFT JOIN @testTable mr03 ON mr03.userId = sr.userId AND mr03.itemName = 'q03'  -- Muli Response for q03
    LEFT JOIN @testTable mr06 ON mr06.userId = sr.userId AND mr06.itemName = 'q06'  -- Muli Response for q06

于 2011-10-25T19:59:51.450 回答
2

不清楚期望的结果应该是什么样子,但一种可能性

; WITH Data AS (
    SELECT
        ROW_NUMBER() OVER (PARTITION BY [userId], [itemName] 
                               ORDER BY [itemValue]) AS RN
        ,  [userId]
        , [itemName]
        , [itemValue]
    FROM 
        @testTable
)
SELECT
    [userId]
    , [q02]
    , [q03]
    , [q05]
FROM
    Data
PIVOT
(
    MIN(itemValue)  
    FOR itemName in ([q02], [q03], [q05])
) AS PivotTable

退货

userId      q02                            q03                            q05
----------- ------------------------------ ------------------------------ ------------------------------
1           1-q02 Answer                   1-q03 Answer 1                 1-q05 Answer
1           NULL                           1-q03 Answer 2                 NULL
1           NULL                           1-q03 Answer 3                 NULL
2           2-q02 Answer                   2-q03 Answer 1                 2-q05 Answer
2           NULL                           2-q03 Answer 2                 NULL
于 2011-10-21T22:05:00.667 回答