行转列

表A,两个字段: CreateDate, Detail
---------------------------------------
       CreateDate           Detail
2009-11-26 00:00:00.000    (A3-5)
2009-11-26 00:00:00.000    (B3-2)
2009-11-27 00:00:00.000    (A3-3)
2009-11-27 00:00:00.000    (B3-1)
---------------变成---------------------
2009-11-26 00:00:00.000    (A3-5)#(B3-2)
2009-11-27 00:00:00.000    (A3-3)#(B3-1)

 

 -------------------------------------
--
  Author : liangCK 梁爱兰
--
  Comment: 小梁 爱 兰儿
--
  Date   : 2009-11-27 11:10:52
--
-----------------------------------

--> 生成测试数据: @tb
DECLARE @tb TABLE (CreateDate datetime,Detail varchar(6))
INSERT INTO @tb
SELECT '2009-11-26 00:00:00.000','(A3-5)' UNION ALL
SELECT '2009-11-26 00:00:00.000','(B3-2)' UNION ALL
SELECT '2009-11-27 00:00:00.000','(A3-3)' UNION ALL
SELECT '2009-11-27 00:00:00.000','(B3-1)'

--SQL查询如下:

SELECT A.CreateDate,STUFF(B.x,1,1,'') AS Details
FROM (SELECT DISTINCT CreateDate FROM @tb) AS A
   
CROSS APPLY (
       
SELECT x=(SELECT '#'+Detail AS [text()]
                 
FROM @tb
                 
WHERE CreateDate=A.CreateDate
                 
FOR XML PATH(''))
    )
AS B

 

 

=============

STUFF ( character_expression , start , length ,character_expression )
参数
character_expression
一个字符数据表达式。character_expression 可以是常量、变量,也可以是字符列或二进制数据列。

start
一个整数值,指定删除和插入的开始位置。如果 start 或 length 为负,则返回空字符串。如果 start 比第一个 character_expression 长,则返回空字符串。start 可以是 bigint 类型。

length
一个整数,指定要删除的字符数。如果 length 比第一个 character_expression 长,则最多删除到最后一个 character_expression 中的最后一个字符。length 可以是 bigint 类型。

返回类型
如果 character_expression 是受支持的字符数据类型,则返回字符数据。如果 character_expression 是一个受支持的 binary 数据类型,则返回二进制数据。

===========================

 

Cross Apply使表可以和表值函数结果进行join,在下面的示例中建了两个表和一个表值函数,T_b的列a_ids中会存放a表的id,分割的字符串连接;我们通过cross apply使T_aT_b表通过splitIDs inner join 连接。请看示例:GO

if object_id('T_a','U') is not null
drop table T_a
GO

CREATE TABLE T_a(    id int unique not null,
    name varchar(50),
)
GO
if object_id('T_b',N'U') is not null
drop table T_b
GO
create table T_b
(
    id int unique not null,
    name varchar(10),
    a_ids varchar(100) null --要在这一列中存放t_a表的ID序列,这样做连第一范式都没有满足,但是有时候考虑性能或设计我们可能会像这么用
)
GO
--初始化数据

INSERT INTO T_a VALUES(1,'A-1')
INSERT INTO T_a VALUES(2,'A-2')
INSERT INTO T_a VALUES(3,'A-3')
INSERT INTO T_a VALUES(4,'A-4')
INSERT INTO T_a VALUES(5,'A-5')

INSERT INTO T_b VALUES(1,'B-1','1,2,4')
GO
--创建一个表值函数,用来拆分用逗号分割的数字串,返回只有一列数字的表
if object_id('splitIDs','TF') is not null
drop function splitIDs;
GO
create function splitIDs(
    @Ids nvarchar(1000)
)
returns @t_id TABLE (id bigint)
as
begin
    declare @i int,@j int,@l int,@v bigint;
    set @i = 0;
    set @j = 0;
    set @l = len(@Ids);
    while(@j < @l)
    begin
       set @j = charindex(',',@Ids,@i+1);
       if(@j = 0) set @j = @l+1;
       set @v = cast(SUBSTRING(@Ids,@i+1,@j-@i-1) as bigint);
       INSERT INTO @t_id VALUES(@v)
       set @i = @j;
    end
    return;
end
GO
--测试splitIDs的执行效果

select * from splitIDs('1,2,4,3')
select * from splitIDs('100')
select * from splitIDs(NULL)
GO
--使用cross apply获得t_b表中指定行对应的所有t_a表中的记录
select 
    aid = t_a.id
    ,aname = t_a.name
    ,bid = t_b.id
from t_b

cross apply splitIDs(a_ids) tbl_Ids
INNER JOIN t_a ON tbl_Ids .id = t_a.id
where t_b.id = 1


备注
如果开始位置或长度值是负数,或者如果开始位置大于第一个字符串的长度,将返回空字符串。如果要删除的长度大于第一个字符串的长度,将删除到第一个字符串中的第一个字符。
如果结果值大于返回类型支持的最大值,则产生错误。


示例
以下示例在第一个字符串 abcdef 中删除从第 2 个位置(字符 b)开始的三个字符,然后在删除的起始位置插入第二个字符串,从而创建并返回一个字符串。

SELECT STUFF('abcdef', 2, 3, 'ijklmn');
GO

下面是结果集:
---------
aijklmnef

(1 row(s) affected)

 

 

=====================

 

FOR XML PATH 语句的应用字符串拼接

 

大家都知道在SQL Server中利用 FOR XML PATH 语句能够把查询的数据生成XML数据,下面是它的一些应用示例。

DECLARE @TempTable table(UserID int , UserName nvarchar(50));

insert into @TempTable (UserID,UserName) values (1,'a')

insert into @TempTable (UserID,UserName) values (2,'b')

 

select UserID,UserName from @TempTable FOR XML PATH

运行这段脚本,将生成如下结果:

<row>

  <UserID>1</UserID>

  <UserName>a</UserName>

</row>

<row>

  <UserID>2</UserID>

  <UserName>b</UserName>

</row>

大家可以看到两行数据生成了两个节点,修改一下PATH的参数:

select UserID,UserName from @TempTable FOR XML PATH('lzy')

再次运行上述脚本,将生成如下的结果:

<lzy>

  <UserID>1</UserID>

  <UserName>a</UserName>

</lzy>

<lzy>

  <UserID>2</UserID>

  <UserName>b</UserName>

</lzy>

可以看到节点变成,其实PATH() 括号内的参数是控制节点名称的,这样的话大家可以看一下如果是空字符串(不是没有参数)会是什么结果?

select UserID,UserName from @TempTable FOR XML PATH('')

执行上面这段脚本将生成结果:

<UserID>1</UserID>

<UserName>a</UserName>

<UserID>2</UserID>

<UserName>b</UserName>

这样就不显示上级节点了,大家知道在 PATH 模式中,列名或列别名被作为 XPath 表达式来处理,也就是说,是列的名字,这样大胆试验一下不给指定列名和别名会是怎么样?

select CAST(UserID AS varchar) + '',UserName + '' from @TempTable FOR XML PATH('')

运行上面这句将生成结果

 

1a2b

 

所有数据都生成一行,而且还没有连接字符,这样的数据可能对大家没有用处,还可以再变化一下:

select CAST(UserID AS varchar) + ',',UserName + '',';' from @TempTable FOR XML PATH('')

生成结果

 

1,a;2,b;

 

大家现在明白了吧,可以通过控制参数来生成自己想要的结果,例如:

select '{' + CAST(UserID AS varchar) + ',','"' +UserName + '"','}' from @TempTable FOR XML PATH('')

生成结果

 

{1,"a"}{2,"b"}

 

还可以生成其他格式,大家可以根据自己需要的格式进行组合。

下面是一个数据统计的应用,希望大家可以通过下面的实例想到更多的应用

DECLARE @T1 table(UserID int , UserName nvarchar(50),CityName nvarchar(50));

insert into @T1 (UserID,UserName,CityName) values (1,'a','上海')

insert into @T1 (UserID,UserName,CityName) values (2,'b','北京')

insert into @T1 (UserID,UserName,CityName) values (3,'c','上海')

insert into @T1 (UserID,UserName,CityName) values (4,'d','北京')

insert into @T1 (UserID,UserName,CityName) values (5,'e','上海')

 

SELECT B.CityName,LEFT(UserList,LEN(UserList)-1) FROM (

SELECT CityName,

    (SELECT UserName+',' FROM @T1 WHERE CityName=A.CityName  FOR XML PATH('')) AS UserList

FROM @T1 A

GROUP BY CityName

) B

生成结果(每个城市的用户名)

 

北京 b,d

上海 a,c,e

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值