2011-05-12 133 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