SQL Pivot第二步-合并行/删除空值

时间:2018-10-15 11:00:41

标签: sql tsql

我正在尝试透视数据。我们从两列开始:ListNum和Value

该行是“诚信”吗?没关系,我只希望所有值向上折叠并删除空值。

在这种情况下,ListNum类似于枚举,其值限制为List1,List2或List3。请注意,它们不按顺序排列(1、3、3、1,2,而不是1,2,3,1,2,3等)。

最好有一个使用标准sql的解决方案,以便它可以在许多数据库中工作。

起点:

+---------+------------+
| ListNum |   Value    |
+---------+------------+
| List1   | A          |
| List3   | 123        |
| List3   | CDE        |
| List1   | Somestring |
| List2   | randString |
+---------+------------+

我能够使用以下方法将列表分为几列:

  select
      case when ListNum = "List1" then Value end as List1,
      case when ListNum = "List2" then Value end as List2,
      case when ListNum = "List3" then Value end as List3
  from Table;

中点:

+------------+------------+-------+
|   List1    |   List2    | List3 |
+------------+------------+-------+
| A          | NULL       | NULL  |
| NULL       | NULL       | 123   |
| NULL       | NULL       | CDE   |
| Somestring | NULL       | NULL  |
| NULL       | randString | NULL  |
+------------+------------+-------+

但是现在我需要向上折叠/删除null以获得- 所需的输出:

+------------+------------+-------+
|   List1    |   List2    | List3 |
+------------+------------+-------+
| A          | randString | 123   |
| Somestring | NULL       | CDE   |
+------------+------------+-------+

3 个答案:

答案 0 :(得分:2)

您是否缺少某种分组标准?您如何确定A属于123而不属于CDE?为什么randString在第一行而不是第二行?

使用这样的分组键很容易:

DECLARE @tbl TABLE(GroupingKey INT, ListNum VARCHAR(100),[Value] VARCHAR(100));
INSERT INTO @tbl VALUES
 (1,'List1','A')
,(1,'List3','123')
,(2,'List3','CDE')
,(2,'List1','Somestring')
,(1,'List2','randString');

SELECT p.*
FROM @tbl
PIVOT
(
    MAX([Value]) FOR ListNum IN(List1,List2,List3)
) p;

但是对于您的数据,这似乎是随机的...

更新:一种随机方法...

以下方法会将值随机地排序到其列中:

DECLARE @tbl TABLE(ListNum VARCHAR(100),[Value] VARCHAR(100));
INSERT INTO @tbl VALUES
 ('List1','A')
,('List3','123')
,('List3','CDE')
,('List1','Somestring')
,('List2','randString');

-这将使用三个独立但编号的集合并将它们加入:

WITH All1 AS (SELECT [Value],ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RandomNumber FROM @tbl WHERE ListNum='List1')
    ,All2 AS (SELECT [Value],ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RandomNumber FROM @tbl WHERE ListNum='List2')
    ,All3 AS (SELECT [Value],ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RandomNumber FROM @tbl WHERE ListNum='List3')
SELECT All1.[Value] AS List1
      ,All2.[Value] AS List2
      ,All3.[Value] AS List3
FROM All1  
FULL OUTER JOIN All2 ON All1.RandomNumber=All2.RandomNumber
FULL OUTER JOIN All3 ON All1.RandomNumber=All3.RandomNumber ;

提示:表中没有隐式排序顺序!

根据您的评论:

  

这只是索引/实例号。 randString是第一个非空行。

没有特定的ORDER BY,相同的SELECT可能会以任何随机顺序返回您的数据。因此,没有第一非空行,至少没有 first的意思在第二 ...

之前

答案 1 :(得分:1)

使用递归CTE的方法可能会起作用:

DECLARE @tbl TABLE(  ListNum VARCHAR(100),[Value] VARCHAR(100));
INSERT INTO @tbl VALUES
 ( 'List1','A')
,( 'List3','123')
,( 'List3','CDE')
,( 'List1','Somestring')
,( 'List2','randString');

DECLARE @mmax int;
SELECT @mmax = cnt from (SELECT TOP 1  count(*) cnt from @tbl group by ListNum ORDER BY count(*) DESC) t;

With rec AS (
    SELECT 1 AS num
    UNION ALL
    SELECT num+1 FROM rec WHERE num+1<=@mmax
)
SELECT t1.List1, t2.List2,  t3.List3  FROM rec
FULL JOIN ( 
    select Value as List1, row_number() over(order by ListNum) rn from  @tbl where ListNum = 'List1'
) t1
ON rec.num = t1.rn
FULL JOIN 
( 
    select Value as List2, row_number() over(order by ListNum) rn from  @tbl where ListNum = 'List2'
) t2
ON rec.num = t2.rn
FULL JOIN 
( 
    select Value as List3, row_number() over(order by ListNum) rn from  @tbl where ListNum = 'List3'
) t3
ON rec.num = t3.rn;

DEMO

答案 2 :(得分:0)

通常,您会使用诸如MAX之类的汇总操作,因为它会隐藏null(除非组中没有其他有效值,否则null永远不会是max)。但是您的查询有点奇怪,因为您没有似乎没有一个可靠的枢轴锚点,并且您正在允许您的任何数据与其他任何内容关联。在现实世界中,这可能不会发生,因为它不是特别有用

更好的示例数据:

Person, Attribute, Value
1, Name, John
1, Age, 10
2, Name, Sarah
3 Age, 39

透视查询:

SELECT
  Person,
  MAX(case when attribute = 'name' then value end) as name,
  MAX(case when attribute = 'age' then value end) as age
FROM
  data
GROUP BY person

结果:

Person, Name, Age
1, John, 10
2, Sarah, NULL
3, NULL, 39