T-SQL:使用多个分隔符解析字符串

时间:2010-03-04 14:26:58

标签: tsql parsing split

我需要能够在SharePoint数据库中查询调查结果。我遇到问题的数据类型是“评级量表”值。因此,每个表格列中的数据代表了一整组子问题及其答案。

以下是一栏中的例子:

1。我们的功能定义了可用性如何衡量生产中的硬件/软件;#3#2。我们的功能存在可用性阈值水平(例如,SLA);#3#3。当存在阈值违规时,我们的函数遵循定义的过程;#4#4。我们的功能收集并维护可用性数据;#4#5。比较分析有助于识别可用性数据的趋势;#4#6。运营级别协议(OLA)指导我们与其他内部团队的互动;#4#

问题以分号结尾,他们的答案在两个#符号内。所以第一个问题的答案是3。

当我导出调查结果时,它将每个问题格式化为列标题,并将答案格式化为下面单元格中的值,这对于获得每个问题的平均值是理想的,并且希望能够复制该问题来自SQL查询。

但如果我能将查询结果分成两列(问题,答案)......我会对此感到非常兴奋。

感谢任何帮助。

非常感谢

Hank Stallings

*****附录:**

这是我的旁观者解决方案的版本......再次感谢!

DECLARE @Table TABLE(  
        QuestionSource VARCHAR(50),  
        QA VARCHAR(5000)  
) 

DECLARE @ReturnTable TABLE( 
        QuestionSource VARCHAR(50), 
        Question VARCHAR(5000),  
        Answer int  
) 

DECLARE @XmlField XML, 
        @QuestionSource VARCHAR(50) 

INSERT INTO @Table SELECT 
'Availability' AS QuestionSource,CONVERT(varchar(5000),ntext1) FROM UserData WHERE tp_ContentType = 'My Survey' 
INSERT INTO @Table SELECT 
'Capacity' AS QuestionSource,CONVERT(varchar(5000),ntext2) FROM UserData WHERE tp_ContentType = 'My Survey' 

--SELECT * FROM @Table 

DECLARE Cur CURSOR FOR  
SELECT  QuestionSource, 
        CAST(Val AS XML) XmlVal  
FROM    (  
            SELECT  QuestionSource, 
            LEFT(Vals, LEN(Vals) - LEN('<option><q>')) Val  
            FROM    (  
                        SELECT  QuestionSource, 
                            '<option><q>' + REPLACE(REPLACE(REPLACE(QA,'&','&amp;'), ';#','</q><a>'), '#', '</a></option><option><q>') Vals  
                        FROM @Table 

                    ) sub  
        ) sub  

OPEN Cur  
FETCH NEXT FROM Cur INTO @QuestionSource,@XmlField  

WHILE @@FETCH_STATUS = 0   
BEGIN  
    INSERT INTO @ReturnTable  
    SELECT  @QuestionSource, 
            T.split.query('q').value('.', 'nvarchar(max)') question,  
            T.split.query('a').value('.', 'nvarchar(max)') answer  
    FROM    @XmlField.nodes('/option') T(split)  
    FETCH NEXT FROM Cur INTO @QuestionSource,@XmlField  
END  

CLOSE Cur  
DEALLOCATE Cur  

SELECT * FROM @ReturnTable 

2 个答案:

答案 0 :(得分:3)

您必须设置分割功能,但是一旦拥有它,请尝试使用此无光标解决方案:

I prefer the number table approach to split a string in TSQL

要使此方法起作用,您需要执行以下一次性表设置:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

设置Numbers表后,创建此split函数,该函数将返回空字符串和行号:

CREATE FUNCTION [dbo].[FN_ListToTableRows]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(

    ----------------
    --SINGLE QUERY-- --this WILL return empty rows
    ----------------
    SELECT
        ROW_NUMBER() OVER(ORDER BY number) AS RowNumber
            ,LTRIM(RTRIM(SUBSTRING(ListValue, number+1, CHARINDEX(@SplitOn, ListValue, number+1)-number - 1))) AS ListValue
        FROM (
                 SELECT @SplitOn + @List + @SplitOn AS ListValue
             ) AS InnerQuery
            INNER JOIN Numbers n ON n.Number < LEN(InnerQuery.ListValue)
        WHERE SUBSTRING(ListValue, number, 1) = @SplitOn
);
GO 

您现在可以轻松地将CSV字符串拆分为表格并加入其中,注意此拆分函数返回空字符串和行号:

select * from dbo.FN_ListToTableRows(',','1,2,3,,,4,5,6777,,,')

输出:

RowNumber            ListValue
-------------------- ------------
1                    1
2                    2
3                    3
4                    
5                    
6                    4
7                    5
8                    6777
9                    
10                   
11                   

(11 row(s) affected)

您现在可以使用CROSS APPLY来分割表格中的每一行,如:

DECLARE @YourTable table (RowID int, RowValue varchar(8000))
INSERT INTO @YourTable VALUES (1,'1. Our function has defined how Availability is measured the hardware/software in Production;#3#2. Availability threshold levels exist for our function (e.g., SLA''s);#3#3. Our function follows a defined process when there are threshold breaches;#4#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#4#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#4#')
INSERT INTO @YourTable VALUES (2,'1. one;#1#2. two;#2#3. three;#3#')
INSERT INTO @YourTable VALUES (3,'1. aaa;#1#2. bbb;#2#3. ccc;#3#')

;WITH AllRows As
(
SELECT
    o.RowID,st.RowNumber,st.ListValue AS RowValue
    FROM @YourTable  o
        CROSS APPLY  dbo.FN_ListToTableRows('#',LEFT(o.RowValue,LEN(o.RowValue)-1)) AS st
)
SELECT
    a.RowID,a.RowValue AS Question, b.RowValue AS Answer
    FROM AllRows                  a
        LEFT OUTER JOIN   AllRows b ON a.RowID=b.RowID AND a.RowNumber+1=b.RowNumber
    WHERE a.RowNumber % 2 = 1 

输出:

RowID       Question                                                                                        Answer
----------- ----------------------------------------------------------------------------------------------- -------
1           1. Our function has defined how Availability is measured the hardware/software in Production;   3
1           2. Availability threshold levels exist for our function (e.g., SLA's);                          3
1           3. Our function follows a defined process when there are threshold breaches;                    4
1           4. Our function collects and maintains Availability data;                                       4
1           5. Comparative analysis helps identify trending with the Availability data;                     4
1           6. Operating Level Agreements (OLA's) guide our interaction with other internal teams;          4
2           1. one;                                                                                         1
2           2. two;                                                                                         2
2           3. three;                                                                                       3
3           1. aaa;                                                                                         1
3           2. bbb;                                                                                         2
3           3. ccc;                                                                                         3

(12 row(s) affected)

答案 1 :(得分:0)

好的,让我们看看。我不得不使用游标,因为这可能是通过C#等编程语言更好地实现的,但是这里...使用Sql Server 2005,尝试以下方法。如果您需要任何解释,请告诉我。

DECLARE @Table TABLE(
        QuestionSource VARCHAR(50),
        QA VARCHAR(1000)
)

DECLARE @ReturnTable TABLE(
        QuestionSource VARCHAR(50),
        Question VARCHAR(1000),
        Answer VARCHAR(10)
)

DECLARE @XmlField XML,
        @QuestionSource VARCHAR(40)

INSERT INTO @Table SELECT
'Availability','1. Our function has defined how Availability is measured the hardware/software in Production;#3#2. Availability threshold levels exist for our function (e.g., SLA''s);#3#3. Our function follows a defined process when there are threshold breaches;#4#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#4#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#4#'
INSERT INTO @Table SELECT
'Capacity', '1. Our function has defined how Availability is measured the hardware/software in Production;#1#2. Availability threshold levels exist for our function (e.g., SLA''s);#2#3. Our function follows a defined process when there are threshold breaches;#3#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#5#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#6#'


DECLARE Cur CURSOR FOR
SELECT  QuestionSource,
        CAST(Val AS XML) XmlVal
FROM    (
            SELECT  QuestionSource,
                    LEFT(Vals, LEN(Vals) - LEN('<option><q>')) Val
            FROM    (
                        SELECT  QuestionSource,
                                '<option><q>' + REPLACE(REPLACE(QA, ';#','</q><a>'), '#', '</a></option><option><q>') Vals
                        FROM    @Table
                    ) sub
        ) sub

OPEN Cur
FETCH NEXT FROM Cur INTO @QuestionSource, @XmlField

WHILE @@FETCH_STATUS = 0 
BEGIN
    INSERT INTO @ReturnTable
    SELECT  @QuestionSource,
            T.split.query('q').value('.', 'nvarchar(max)') question,
            T.split.query('a').value('.', 'nvarchar(max)') answer
    FROM    @XmlField.nodes('/option') T(split)
    FETCH NEXT FROM Cur INTO @QuestionSource, @XmlField
END

CLOSE Cur
DEALLOCATE Cur

SELECT  * 
FROM    @ReturnTable