使用交叉应用将列转置为行
Posted
技术标签:
【中文标题】使用交叉应用将列转置为行【英文标题】:transpose columns to row using cross apply 【发布时间】:2019-03-11 11:36:41 【问题描述】:原数据
+--------+-------+---------------+------------+--------+--------------+-------+
| RowNum | SeqNo | Name | NameReason | Gender | GenderReason | ID |
+--------+-------+---------------+------------+--------+--------------+-------+
| 1 | A123 | IronMan | | P | | E8888 |
| 2 | A123 | CaptainMarvel | A | L | A | E8888 |
| 3 | A123 | Yoooo | | | | E8888 |
| 4 | A123 | Heyyy | | | | E8888 |
| 1 | B456 | Hey | | | | D2222 |
| 2 | B456 | DOTS | A | | | D2222 |
| 1 | C1234 | Hulk | | | | E8989 |
| 2 | C1234 | Cap | | | | E8989 |
| 3 | C1234 | Hat | | | | E8989 |
+--------+-------+---------------+------------+--------+--------------+-------+
我想要的结果
+-------+-------+---------+---------------+----------+--------+
| SeqNo | ID | ColName | From_Value | To_Value | Reason |
+-------+-------+---------+---------------+----------+--------+
| A123 | E8888 | Name | CaptainMarvel | IronMan | A |
| A123 | E8888 | Gender | L | P | A |
| B456 | D2222 | Name | DOTS | Hey | A |
| C1234 | E8989 | Name | Cap | Hulk | |
+-------+-------+---------+---------------+----------+--------+
查询:
select a.rownum, a.seqno, a.name, a.id,
b.*
from #A a cross apply
( values ('Name', NameReason)
('Gender', GenderReason)
) b (colname, Reason)
where reason is not null
注意:要查找更改。 RowNum=1
是最新更新 (To_Value
),RowNum=2
(From_Value
)。 Rownum 已经过滤了顶部更新的结果 (rownumber () over (partitition)
。 我只需要 rownum=2(from_Value), rownum=1(to_value) 其他人忽略, 因为 1 个应用程序可以更新 100 次,我只需要找到最新的更改即可。
从上面的查询中,如何修改为我想要的结果?如何添加from_value和to_value,原因?
【问题讨论】:
请解释一下转换的逻辑。不明显。 @GordonLinoff 嗨。我只是想知道有什么变化。 RowNum=1 绝对是最新的更新。我想要的结果,From_Value=RowNum=2,To_Value=RowNum=1。我只想知道如何修改我的脚本以便满足 From_Value 和 To_Value 【参考方案1】:假设您可以链接 RowNum + 1 上的“From”记录
SELECT q.SeqNo, q.ToName AS Name, q.ID, ca.ColName, ca.From_Value, ca.To_Value
FROM
(
SELECT
a1.ID, a1.SeqNo, a1.NameReason, a1.GenderReason, a1.RowNum,
a2.Name as FromName, a1.Name as ToName,
a2.Gender AS FromGender, a1.Gender AS ToGender
FROM #A a1
JOIN #A a2 ON (a2.ID = a1.ID AND a2.RowNum = a1.RowNum + 1)
WHERE (a1.NameReason IS NOT NULL OR a1.GenderReason IS NOT NULL)
AND a1.Name != a2.Name
) q
CROSS APPLY (VALUES
(1, 'Name', NameReason, FromName, ToName),
(2, 'Gender', GenderReason, FromGender, ToGender)
) ca (ColSeq, ColName, Reason, From_Value, To_Value)
WHERE ca.Reason IS NOT NULL
ORDER BY q.SeqNo, ca.ColSeq;
但根据 cmets,只有 RowNum 1 和 2 重要。 然后可以简化。
SELECT q.SeqNo, q.ToName AS Name, q.ID, ca.ColName, ca.From_Value, ca.To_Value
FROM
(
SELECT
a1.ID, a1.SeqNo,
a1.NameReason, a2.Name as FromName, a1.Name as ToName,
a1.GenderReason, a2.Gender AS FromGender, a1.Gender AS ToGender
FROM #A a1
JOIN #A a2 ON (a2.ID = a1.ID AND a2.RowNum = 2)
WHERE a1.RowNum = 1
) q
CROSS APPLY (VALUES
(1, 'Name', NameReason, FromName, ToName),
(2, 'Gender', GenderReason, FromGender, ToGender)
) ca (ColSeq, ColName, Reason, From_Value, To_Value)
WHERE ca.Reason IS NOT NULL
ORDER BY q.SeqNo, ca.ColSeq;
结果:
SeqNo Name ID ColName From_Value To_Value
----- ------- ----- ------- ------------- --------
A123 IronMan E8888 Name CaptainMarvel IronMan
A123 IronMan E8888 Gender L P
B456 Hey D2222 Name DOTS Hey
dbfiddle here
的测试【讨论】:
如果我只想要 seqno、id、colname、from_value、to_value、reason 怎么办?使用此查询,它会显示所有更改。我只想要rownum = 1和rownum = 2。 2(from_value) > 1(to_value) 其他人忽略它。因为 1 个应用程序我可能更新了 100 次(不可能全部显示,所以只需要前 2 个)就可以了。这些 rownum 进行了过度分区(按日期和 idno 排序) 哦...好吧,这在这个 SQL 中很容易做到。只需更改子查询中的 WHERE 子句。WHERE a1.RowNum = 1
似乎工作正常。我需要时间来“吸收又理解”查询。
如果 RowNum 是通过 ROW_NUMBER(PARTITION BY ID ORDER BY [date] desc)
计算的,那么 a2 上的 JOIN 不需要 AND a2.SeqNo = a1.SeqNo
@luke 你的回答很棒。但是,如果我有理由命名并更新 2 次,我只需要最近的更改,我该如何修改它。顺便说一句,我使用你的第一个脚本而不是 1。【参考方案2】:
您可以使用UNION
编写如下查询。
select t1.SeqNo,
t1.Name,
t1.ID,
'Name' as ColName,
(select name
from mytable mt
where mt.SeqNo = t1.seqNo
and RowNum = 2) as From_Value,
t1.Name as To_Value
from mytable t1
where t1.RowNum = 1
union all
select *
from (select t1.SeqNo,
t1.Name,
t1.ID,
'Gender' as ColName,
t1.Gender as From_Value,
(select Gender
from mytable mt
where mt.SeqNo = t1.seqNo
and RowNum = 1) as To_Value
from mytable t1
where t1.RowNum = 2) t
where From_Value is not null
Online Demo
输出
+-------+---------------+-------+---------+---------------+----------+
| SeqNo | Name | ID | ColName | From_Value | To_Value |
+-------+---------------+-------+---------+---------------+----------+
| A123 | CaptainMarvel | E8888 | Gender | L | P |
+-------+---------------+-------+---------+---------------+----------+
| A123 | IronMan | E8888 | Name | CaptainMarvel | IronMan |
+-------+---------------+-------+---------+---------------+----------+
| B456 | Hey | D2222 | Name | DOTS | Hey |
+-------+---------------+-------+---------+---------------+----------+
【讨论】:
我正在处理 10mils 记录 + 20+ cols。当我运行您的脚本时,出现错误“子查询返回 > 1 值” 您可以使用 LEAD 和 LAG以上是关于使用交叉应用将列转置为行的主要内容,如果未能解决你的问题,请参考以下文章