2011-05-12 81 views
2

表名:表1一列拆分为更多列的SQL Server 2008?

id name 
1 1-aaa-14 milan road 
2 23-abcde-lsd road 
3 2-mnbvcx-welcoome street 

我想是这样的结果:

Id name name1 name2 
1 1  aaa  14 milan road 
2 23  abcde lsd road 
3 2  mnbvcx welcoome street 

回答

3

这个功能应该给你你需要什么。

--Drop Function Dbo.Part 
Create Function Dbo.Part 
    (@Value Varchar(8000) 
    ,@Part Int 
    ,@Sep Char(1)='-' 
)Returns Varchar(8000) 
As Begin 
Declare @Start Int 
Declare @Finish Int 
Set @Start=1 
Set @Finish=CharIndex(@Sep,@Value,@Start) 
While (@Part>1 And @Finish>0)Begin 
    Set @[email protected]+1 
    Set @Finish=CharIndex(@Sep,@Value,@Start) 
    Set @[email protected] 
End 
If @Part>1 Set @Start=Len(@Value)+1 -- Not found 
If @Finish=0 Set @Finish=Len(@Value)+1 -- Last token on line 
Return SubString(@Value,@Start,@[email protected]) 
End 

用法:

Select ID 
     ,Dbo.Part(Name,1,Default)As Name 
     ,Dbo.Part(Name,2,Default)As Name1 
     ,Dbo.Part(Name,3,Default)As Name2 
    From Dbo.Table1 

这是相当计算密集型的,因此,如果表1是非常长的,你应该写结果到另一个表,你可以从时间刷新时间(也许一次一天,晚上)。

更好的是,您可以创建一个触发器,每当对Table1进行更改时都会自动更新Table2。假设列ID是主键:

Create Table Dbo.Table2(
    ID Int Constraint PK_Table2 Primary Key, 
    Name Varchar(8000), 
    Name1 Varchar(8000), 
    Name2 Varchar(8000)) 
Create Trigger Trigger_Table1 on Dbo.Table1 After Insert,Update,Delete 
As Begin 
If (Select Count(*)From Deleted)>0 
    Delete From Dbo.Table2 Where ID=(Select ID From Deleted) 
If (Select Count(*)From Inserted)>0 
    Insert Dbo.Table2(ID, Name, Name1, Name2) 
    Select ID 
      ,Dbo.Part(Name,1,Default) 
      ,Dbo.Part(Name,2,Default) 
      ,Dbo.Part(Name,3,Default) 
     From Inserted 
End 

现在,做你的数据操作(插入,更新,删除)上表1,但是做你的上表2 Select语句来代替。

+0

这是一个很好的方法 – Ravia 2013-02-04 09:32:55

0

如果你总是将有2个破折号,您可以通过使用PARSENAME

--testing table 
CREATE TABLE #test(id INT, NAME VARCHAR(1000)) 


INSERT #test VALUES(1, '1-aaa-14 milan road') 
INSERT #test VALUES(2, '23-abcde-lsd road') 
INSERT #test VALUES(3, '2-mnbvcx-welcoome street') 

SELECT id,PARSENAME(name,3) AS name, 
PARSENAME(name,2) AS name1, 
PARSENAME(name,1)AS name2 
FROM (
SELECT id,REPLACE(NAME,'-','.') NAME 
FROM #test)x 

执行以下操作如果名称列中有点,则必须先替换它们,然后将它们替换回最后的点

例如,通过使用一个波浪线来替代点

INSERT #test VALUES(3, '5-mnbvcx-welcoome street.') 


SELECT id,REPLACE(PARSENAME(name,3),'~','.') AS name, 
REPLACE(PARSENAME(name,2),'~','.') AS name1, 
REPLACE(PARSENAME(name,1),'~','.') AS name2 
FROM (
SELECT id,REPLACE(REPLACE(NAME,'.','~'),'-','.') NAME 
FROM #test)x 
+0

还算不错,但如果你总是有3个或更少的破折号它只能。哦,如果你没有任何时期(正如你指出的那样,你可以更换时期,然后恢复它们)。如果你没有[方括号]。而且...我不确定ParseName如何处理以空格开头或结尾的名称部分,是吗?一般来说,ParseName函数不是为了这个目的;它专门用于数据库对象名称。 – 2011-05-12 18:04:21

+0

这正是我所说的 – SQLMenace 2011-05-12 18:07:51

+0

在我以前的评论中,只要我输入第一个转折点,我就错误地按下了“添加评论”。我立刻编辑完成了我的想法。我认为@SQLMenace回复了我评论的第一个版本,所以如果没有意义,那是我的错。 – 2011-05-12 18:10:59

1

以下解决方案使用recursiveCTE分割字符串,而PIVOT用于在各自的列中显示零件。

WITH Table1 (id, name) AS (
    SELECT 1, '1-aaa-14 milan road' UNION ALL 
    SELECT 2, '23-abcde-lsd road' UNION ALL 
    SELECT 3, '2-mnbvcx-welcoome street' 
), 
cutpositions AS (
    SELECT 
    id, name, 
    rownum = 1, 
    startpos = 1, 
    nextdash = CHARINDEX('-', name + '-') 
    FROM Table1 
    UNION ALL 
    SELECT 
    id, name, 
    rownum + 1, 
    nextdash + 1, 
    CHARINDEX('-', name + '-', nextdash + 1) 
    FROM cutpositions c 
    WHERE nextdash < LEN(name) 
) 
SELECT 
    id, 
    [1] AS name, 
    [2] AS name1, 
    [3] AS name2 
    /* add more columns here */ 
FROM (
    SELECT 
    id, rownum, 
    part = SUBSTRING(name, startpos, nextdash - startpos) 
    FROM cutpositions 
) s 
PIVOT (MAX(part) FOR rownum IN ([1], [2], [3] /* extend the list here */)) x 

在不额外修改这个查询可以分割的名字由多达100份(这是默认的最大递归深度,这是可以改变的),但只能显示不超过3他们。您可以轻松地将其扩展到您希望显示的多个部分,只需按照评论中的说明操作即可。

1
select T.id, 
    substring(T.Name, 1, D1.Pos-1) as Name, 
    substring(T.Name, D1.Pos+1, D2.Pos-D1.Pos-1) as Name1, 
    substring(T.Name, D2.Pos+1, len(T.name)) as Name2 
from Table1 as T 
    cross apply (select charindex('-', T.Name, 1)) as D1(Pos) 
    cross apply (select charindex('-', T.Name, D1.Pos+1)) as D2(Pos) 
建议的解决方案

测试性能

设置:

create table Table1 
(
    id int identity primary key, 
    Name varchar(50) 
) 
go 

insert into Table1 
select '1-aaa-14 milan road' union all 
select '23-abcde-lsd road' union all 
select '2-mnbvcx-welcoome street' 

go 10000 

结果:

enter image description here

+0

我知道应该可以用CROSS APPLY!我对这项技术还不甚了解。无论如何,我的解决方案可能更快。 :) – 2011-05-13 06:51:08

+0

@Andriy--对性能进行了一些测试:) – 2011-05-13 09:31:05

+0

在我的假设以及之前关于比较结果的报告中,我有点太仓促。我得到的实际数字与您的数据完全一致。对不起,我是如此误导。并感谢您的测试和发布结果! – 2011-05-13 13:00:50