2010-09-07 50 views
9

एक मेज को देखते हुए का उपयोग किए बिना एसक्यूएल सर्वर में CSV कॉलम से एक तालिका बनाएं:एक कर्सर

|Name | Hobbies    | 
----------------------------------- 
|Joe  | Eating,Running,Golf | 
|Dafydd | Swimming,Coding,Gaming | 

मैं इन पंक्तियों बाहर विभाजित करने के लिए चाहते हैं पाने के लिए:

|Name | Hobby  | 
---------------------- 
|Joe  | Eating | 
|Joe  | Running | 
|Joe  | Golf  | 
|Dafydd | Swimming | 
|Dafydd | Coding | 
|Dafydd | Gaming | 

लिए मैंने नीचे पूरा कर लिया है (उदाहरण एसएसएमएस में चलाने के लिए तैयार है), मेरा समाधान एक कर्सर का उपयोग करता है जो मुझे लगता है कि बदसूरत है। क्या ऐसा करने का कोई बेहतर तरीका है? मैं SQL Server 2008 R2 पर हूं यदि कुछ नया है जो मेरी मदद करेगा।

धन्यवाद

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Split]') and xtype in (N'FN', N'IF', N'TF')) drop function [dbo].Split 
go 
CREATE FUNCTION dbo.Split (@sep char(1), @s varchar(512)) 
RETURNS table 
AS 
RETURN (
    WITH Pieces(pn, start, stop) AS (
     SELECT 1, 1, CHARINDEX(@sep, @s) 
     UNION ALL 
     SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1) 
     FROM Pieces 
     WHERE stop > 0 
    ) 
    SELECT pn, 
     SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s 
    FROM Pieces 
) 


go 

declare @inputtable table (
    name varchar(200) not null, 
    hobbies varchar(200) not null 
) 

declare @outputtable table (
    name varchar(200) not null, 
    hobby varchar(200) not null 
) 

insert into @inputtable values('Joe', 'Eating,Running,Golf') 
insert into @inputtable values('Dafydd', 'Swimming,Coding,Gaming') 

select * from @inputtable 

declare inputcursor cursor for 
select name, hobbies 
from @inputtable 

open inputcursor 

declare @name varchar(255), @hobbiescsv varchar(255) 
fetch next from inputcursor into @name, @hobbiescsv 
while(@@FETCH_STATUS <> -1) begin 

    insert into @outputtable 
    select @name, splithobbies.s 
    from dbo.split(',', @hobbiescsv) splithobbies 

    fetch next from inputcursor into @name, @hobbiescsv 
end 
close inputcursor 
deallocate inputcursor 

select * from @outputtable 
+0

हालांकि आपके उदाहरण में प्रत्येक व्यक्ति के पास वास्तव में तीन शौक हैं, मुझे लगता है कि प्रत्येक व्यक्ति के पास वास्तव में 1 या अधिक शौक हो सकते हैं? – LittleBobbyTables

+0

@LittleBobbyTables हाँ, मुझे यह उल्लेख करना चाहिए था कि शौक सूची मनमानी लंबाई – amarsuperstar

उत्तर

9

उपयोग एक तरह की स्ट्रिंग पार्स समारोह here पाया। आपकी बेस तालिका में प्रत्येक पंक्ति के लिए फ़ंक्शन निष्पादित करने के लिए CROSS APPLY का उपयोग करना महत्वपूर्ण है।

CREATE FUNCTION [dbo].[fnParseStringTSQL] (@string NVARCHAR(MAX),@separator NCHAR(1)) 
RETURNS @parsedString TABLE (string NVARCHAR(MAX)) 
AS 
BEGIN 
    DECLARE @position int 
    SET @position = 1 
    SET @string = @string + @separator 
    WHILE charindex(@separator,@string,@position) <> 0 
     BEGIN 
     INSERT into @parsedString 
     SELECT substring(@string, @position, charindex(@separator,@string,@position) - @position) 
     SET @position = charindex(@separator,@string,@position) + 1 
     END 
    RETURN 
END 
go 

declare @MyTable table (
    Name char(10), 
    Hobbies varchar(100) 
) 

insert into @MyTable 
    (Name, Hobbies) 
    select 'Joe', 'Eating,Running,Golf' 
    union all 
    select 'Dafydd', 'Swimming,Coding,Gaming' 

select t.Name, p.String 
    from @mytable t 
     cross apply dbo.fnParseStringTSQL(t.Hobbies, ',') p 

DROP FUNCTION [dbo].[fnParseStringTSQL] 
+0

संपादित हो सकती है: आपको अंक दिए गए, मैं स्पष्ट रूप से समय नहीं पढ़ सकता! – amarsuperstar

2

अपने DB में इस समारोह बनाएँ:

CREATE FUNCTION dbo.Split(@origString varchar(max), @Delimiter char(1))  
returns @temptable TABLE (items varchar(max))  
as  
begin  
    declare @idx int  
    declare @split varchar(max)  

    select @idx = 1  
     if len(@origString)<1 or @origString is null return  

    while @idx!= 0  
    begin  
     set @idx = charindex(@Delimiter,@origString)  
     if @idx!=0  
      set @split= left(@origString,@idx - 1)  
     else  
      set @split= @origString 

     if(len(@split)>0) 
      insert into @temptable(Items) values(@split)  

     set @origString= right(@origString,len(@origString) - @idx)  
     if len(@origString) = 0 break  
    end 
return  
end 

और फिर बस अपने कथन का चयन करें में इसे कहते और क्रॉस का उपयोग निम्नलिखित समारोह

Select t.Name, 
     s.items as 'Hobby' 
from dbo.MyTable as t 
Cross Apply dbo.Split(t.Hobbies,',') as s 
+0

Nopers। आपको या तो टीवीएफ लागू करने या बाहरी रूप से लागू करना होगा। –

+0

@ डेनिस - धन्यवाद डेनिस। भूल गया कि मैं वहां टेबल फ़ंक्शन से निपट रहा था। अब मेरा जवाब अपडेट किया गया। – codingbadger

+0

धन्यवाद, 'क्रॉस लागू' वह है जो मैं बाद में था! अब – amarsuperstar

4

बस करने के लिए शामिल होने के लिए लागू होते हैं :

select * 
from @inputtable 
outer apply dbo.split(',', hobbies) splithobbies 
+0

पर कुछ और पढ़ना होगा SQL सर्वर (जो बेकार है) में कोई मूल "विभाजन" फ़ंक्शन नहीं है। –

+2

@ फिलिप उत्तर को प्रश्न के संदर्भ में दिया गया है। क्या आपने देखा कि प्रश्न की शुरुआत में पहले से ही एक विभाजन समारोह लागू किया गया है? तो, यह ठीक है। –

+0

सही है, बाहरी आवेदन वह हिस्सा है जिसे मुझे जानने की आवश्यकता है, इसलिए यह उत्तर पूरी तरह मान्य है – amarsuperstar

0

I आम तौर पर तालिका मूल्यवान प्रारूप में सीएसवी सूची को विभाजित करने के लिए XML का उपयोग करना पसंद करते हैं।

CREATE FUNCTION dbo.SplitStrings_XML 
(
    @List  NVARCHAR(MAX), 
    @Delimiter NVARCHAR(255) 
) 
RETURNS TABLE 
WITH SCHEMABINDING 
AS 
    RETURN 
    ( 
     SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)') 
     FROM 
     ( 
     SELECT x = CONVERT(XML, '<i>' 
      + REPLACE(@List, @Delimiter, '</i><i>') 
      + '</i>').query('.') 
    ) AS a CROSS APPLY x.nodes('i') AS y(i) 
    ); 
GO 

और अधिक ऐसा करने के तरीके दिखा तकनीकों के लिए निम्नलिखित article: आप इस समारोह देख सकते हैं। फिर, आपको फ़ंक्शन को लागू करने के लिए केवल CROSS APPLY खंड का उपयोग करने की आवश्यकता है।