2015-03-31 86 views
1

我新的SQL Server和有问题牛逼SQL:选择每个匹配的行为

这里是我的底线的情况:

表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,并显示他们在一个单独的列。

+0

为什么是第二排的结果呢? – 2015-03-31 15:50:45

+0

@GiorgiNakeuri sry,结果第二排是错误的。 – hunt3r87 2015-04-01 08:54:27

回答

2

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

的根据DBA“正确的方式”会做这样的事情:

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

然后,在你的应用程序,走过你的结果集,当你需要它格式化输出。

你甚至可以包括对pid内的每个id命令,也许使它更容易一点:

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做到这一点(例如,您的报告软件不能处理这种事情,这是你要做的),你知道你永远不会有超过3 id每个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是否在第五位或第五位。他们完全没有结果。我提到的第一种方法将始终返回所有数据,并且可以编写应用程序来轻松处理。

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

+0

非常感谢,你真的帮了我。我将在应用程序端组织输出,但这是另一个话题。 – hunt3r87 2015-04-01 09:14:25

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