T SQL:选择每个匹配的行作为列

时间:2015-03-31 15:39:43

标签: sql-server tsql

我是SQL Server新手并遇到问题

以下是我的基线情况:

表foo:

id | pid  | val1 | val2 | val3
------------------------------
1  | 4721 |  1   |  2   |  3
2  |   25 |  4   |  5   |  6
3  | 4721 |  7   |  8   |  9

结果表:

pid | id_1 | val1_1 | val2_1 | val3_1 | id_2 | val1_2 | val2_2 | val3_2 | id_3 | val1_3 | val2_3 | val3_3
----------------------------------------------------
4721 | 1  |  1   |  2   |  3  | 2 | 4 | 5 | 6 | 3 | 7 | 8 | 9

我想要的是选择所有匹配的行为pid = 4721,并在单独的列中显示它们。

2 个答案:

答案 0 :(得分:2)

严格来说,这个问题的SQL答案是,"这是一个显示问题。显示是一个应用程序问题,而不是数据库问题。"从关系角度来说,这也要求服务器打破First Normal Form并创建重复组,这意味着它几乎肯定需要跳过几个圈,并且会有很大的局限性。

"正确的方式"根据DBA的说法,可能会这样做:

SELECT pid, id, val1, val2, val3
FROM Table
ORDER BY pid, id;

然后,在您的应用程序中,浏览结果集并根据需要格式化输出。

您甚至可以在id中为每个pid添加订单,以使其更轻松一些:

SELECT pid, 
    id, 
    val1, 
    val2, 
    val3,
    ROW_NUMBER() OVER (PARTITION BY pid, ORDER BY id) AS "id_order"
FROM Table
ORDER BY pid, id;

但是,我们说你做不到。

如果您绝对 使用SQL执行此操作(例如,您的报告软件无法处理此类事情,并且您已获得)并且你知道每个id你永远不会超过3 pid,你可以尝试这样的事情:

;WITH Table_id_ordered AS (
    SELECT pid, 
        id, 
        val1, 
        val2, 
        val3,
        ROW_NUMBER() OVER (PARTITION BY pid, ORDER BY id) AS "id_order"
    FROM Table
)
SELECT t1.pid,
    t1.id   as id_1,
    t1.val1 as val1_1,
    t1.val2 as val2_1,
    t1.val3 as val3_1,
    t2.id   as id_2,
    t2.val1 as val1_2,
    t2.val2 as val2_2,
    t2.val3 as val3_2,
    t3.id   as id_3,
    t3.val1 as val1_3,
    t3.val2 as val2_3,
    t3.val3 as val3_3
FROM Table_id_ordered t1
LEFT JOIN Table_id_ordered t2
    ON t2.pid = t1.pid
    AND t2.id_order = t1.id_order + 1
LEFT JOIN Table_id_ordered t3
    ON t3.pid = t2.pid
    AND t3.id_order = t2.id_order + 1
WHERE t1.id_order = 1;

显然,对于任何id,最多只能有pid个{}。如上所述,它也不会告诉你表格中id是否会在第五或第五位。他们只是完全没有结果。我提到的第一种方法将始终返回所有数据,并且可以编写应用程序以便轻松处理。

可以为id的任意数量的pid创建动态解决方案,但这些解决方案要复杂得多。

答案 1 :(得分:0)

我首先取消隐藏数据以获取列和值列表,分配新列名,然后动态转动数据。这适用于任何值的组合。

注意:我略微更改了列名,因此他们可以正确排序。

IF OBJECT_ID('tempdb..#yourTable') IS NOT NULL
    DROP TABLE #yourTable;
IF OBJECT_ID('tempdb..#UnpivotTable') IS NOT NULL
    DROP TABLE #UnpivotTable;

SELECT * INTO #yourTable
FROM
(
    SELECT 1,4721,1,2,3
    UNION ALL 
    SELECT 2,25,4,5,6
    UNION ALL
    SELECT 3,4721,7,8,9
) AS  bar(id,pID,val1,val2,val3);

DECLARE @cols VARCHAR(MAX);


SELECT  pID,
        --This is where I create the column names
        CONCAT(ROW_NUMBER() OVER (PARTITION BY pID,col ORDER BY pID,id2),'_',col) NewColName,
        val INTO #UnpivotTable
FROM 
(
    --I need ID2 for ROW_NUMBER() so the NewColNames are applied to the correct values
    SELECT *,id as ID2
    FROM #yourTable
) A
UNPIVOT
(
    val FOR col IN(ID,val1,val2,val3)
) unpvt


--Puts columns in alphabetic order into @cols
SELECT @cols = COALESCE(@cols + ',','') + QUOTENAME(NewColName)
FROM #UnpivotTable
--Group by gets rid of any duplicate column names
GROUP BY NewColName
ORDER BY NewColName

EXEC
(
    'SELECT *
    FROM #UnpivotTable
    PIVOT
    (
        MAX(val) FOR newColName IN (' + @cols + ')
    ) pvt
    WHERE pID = 4721'
)

--Cleanup
IF OBJECT_ID('tempdb..#yourTable') IS NOT NULL
    DROP TABLE #yourTable;
IF OBJECT_ID('tempdb..#UnpivotTable') IS NOT NULL
    DROP TABLE #UnpivotTable;

结果:

pID         1_id        1_val1      1_val2      1_val3      2_id        2_val1      2_val2      2_val3
----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
4721        1           1           2           3           3           7           8           9