将一行拆分成多行

3

我有一个表格:

+---------+------+---------+
|  Col1   | Col2 |  Col3   |
+---------+------+---------+
| PersonA | $10  | PersonB |
| PersonC | $20  | PersonD |
+---------+------+---------+

我希望得到以下类似的结果:

+---------+-----+-----+
|  Col1   | Db  | Cr  |
+---------+-----+-----+
| PersonA | 0   | $10 |
| PersonB | $10 | 0   |
| PersonC | 0   | $20 |
| PersonD | $20 | 0   |
+---------+-----+-----+

是否有不使用Union all的方法?


1
什么是数据库管理系统(DBMS)? - DxTx
1
为什么不用UNION ALL?在这里似乎是正确的工具... - sticky bit
由于存在数百万条记录,Union All 将再次读取同一张表。因此,我在想是否有任何不使用Union All的方式。DBMS为mssql。 - Teknas
再次读取同一张表有什么问题吗?我真的不明白你的担忧是什么? - ADyson
3个回答

4
如果col1代表CR,col3代表DB,您可以使用以下UNION ALL来获得所需的输出结果-
SELECT col1 PersonName, 
'$0' DB,
col2 CR    
FROM your_table

UNION ALL

SELECT col3 PersonName,
col2 DB, 
'$0' CR    
FROM your_table

为了保持交易顺序与原始顺序一致,您需要先创建ROW_NUMBER,然后按RN排序。不同数据库的ROW_NUMBER创建方式各不相同。由于您没有提到数据库名称,因此我无法在此处添加该逻辑。
如果您想避免使用UNION ALL,可以考虑使用UNPIVOT。如果您正在使用MSSQL,请按照以下方法操作-
WITH CTE AS
(
    SELECT U.PersonName,
    CASE WHEN U.name = 'Col3' THEN U.Col2 ELSE '$0' END AS Db,
    CASE WHEN U.name = 'Col1' THEN U.Col2 ELSE '$0' END AS Cr,
    U.name ValuesFromColumn -- Just print this for better understanding
    FROM your_table S
    UNPIVOT
    (
        PersonName
        FOR NAME IN (col1,col3)
    ) U
)

-- Now select from CTE and Join other table as

SELECT * 
FROM CTE A
LEFT JOIN Other_Table B ON....

-- OR Like

SELECT * 
FROM Other_Table A
LEFT JOIN CTE B ON....

UNPIVOT 在其他数据库中也是可用的。如果您使用其他数据库,可以相应地调整语法。


由于有数百万条记录,Union All 将会再次读取相同的表格。所以我在想是否有一种没有使用 Union All 的方法。 - Teknas
你的数据库是什么? - mkRabbani
一个问题,我能否将此输出结果与其他表进行左连接?这个结果应该在左连接的右侧。假设我想要在其他表中选择Person ID,选择PersonID、Person Name、Db、Cr从PersonMaster左连接[your_table S] - Teknas
当然可以,只需在CTE中保留完整内容,并使用JOIN将任何内容与CTE连接即可。 - mkRabbani
我尝试使用左连接,但可能做错了,没有得到结果。我想让表X与unpivot的结果进行左连接。例如,表X将包含所有个人ID和姓名,而unpivot将只有姓名,因此需要在表X中列出所有人,并将其与unpivot结果进行左连接(select .....from tableX left join unpivotresult on x.name = unpivot.name)。 - Teknas

2
你可以使用MSSQL中的unpivot来实现此目的,因为你想将列转换为行。请查看此链接以了解有关SQL中的unpivot更多信息。UNPIVOT
使用带有caseunpivot即可获得所需结果。
declare @tab table ( cola varchar(50), colb int, colc varchar(50))

insert into @tab ( cola, colb, colc )
values ( 'A', 10, 'B')
, ( 'C', 20, 'D' )

select name,
case when colname = 'colc' then colb else '0' end as debit,
case when colname = 'cola' then colb else '0' end as credit
FROM @tab S
UNPIVOT
(
    name
    FOR colname  IN (cola,colc)
) U;

一个问题,我能否将此输出结果与其他表进行左连接?这个结果应该在左连接的右侧。假设我想要在其他表中选择Person ID,选择PersonID、Person Name、debit、credit从PersonMaster左连接(你上面的@tab)。 - Teknas

0
使用 apply:
select v.*
from t cross apply
     (values (t.col1, t.col2, 0),
             (t.col3, 0, t.col2)
     ) v(col1, db, cr);

横向连接(apply 实现的内容)非常强大。在这种情况下,它应该是解决此问题最简洁、性能最佳的方法。

这里有一个 db<>fiddle。

编辑:

您可以继续添加连接或 apply

select v.*
from t cross apply
     (values (t.col1, t.col2, 0),
             (t.col3, 0, t.col2)
     ) v(col1, db, cr) left join
     x
     on x.? = v.?;

这个可以用,但是有一个问题:我怎么能够在LEFT JOIN中使用这个输出呢?v.col1需要与另一个表进行左连接。 - Teknas
@user1041627,只需添加left join x on x.? = v.? - Gordon Linoff
无法工作,您的查询中写着“from t cross apply”,所以我只能使用“t left join x on t.? = x.?” ////// 它不允许将v与表t绑定。 - Teknas
我可以在左侧使用表X吗?从表X中获取所有内容并与表V匹配......从X左连接V选择x.name、v.db、v.cr(v是交叉应用结果) - Teknas
@user1041627……就像在FROM子句中的任何其他派生表或表引用一样。 - Gordon Linoff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接