如何在SQL中使用单行查询拆分字符串(带分隔符)而不使用用户定义的函数?

时间:2014-03-13 01:24:07

标签: sql sql-server split user-defined-functions delimiter

方案


The table structure of E_STProcedure

根据上表 STProcedure 中显示的现有场景,可能存在与单个 STProcedure 相关的组合组。在上图中可以看出,以下关系适用于 STProcedureID GroupID

  1. A => A_B_C
  2. B => B_A_E
  3. C =>
  4. D => D_B_C_E
  5. E => D_E_A
  6. 要求+初始解决方案


    要求 GroupID 应在单行查询中使用分隔符“_”进行拆分。
    我能够通过使用用户定义的函数找到解决方案,如下所示

    • 用户定义的功能 - fnSplit
    ALTER FUNCTION  E1.fnSplit (@sInputList udtStringMax, @sDelimiter udtStringMax = '_') 
                                    RETURNS @List TABLE (item udtStringMax,CNT INT IDENTITY(1,1))
    
    BEGIN
        DECLARE @sItem udtStringMax
        WHILE CHARINDEX(@sDelimiter,@sInputList,0) <> 0
        BEGIN
            SELECT
                @sItem=SUBSTRING(@sInputList,1,CHARINDEX(@sDelimiter,@sInputList,0)-1),
                @sInputList=SUBSTRING(@sInputList,CHARINDEX(@sDelimiter,@sInputList,0)+LEN(@sDelimiter),LEN(@sInputList))
    
            IF LEN(@sItem) > 0
                INSERT INTO @List SELECT @sItem
        END
    
        IF LEN(@sInputList) > 0
            INSERT INTO @List SELECT @sInputList -- Put the last item in
        RETURN
    END
    
    • 单行查询如下所示,使用了fnSplit
    SELECT * FROM E1.E_STProcedure WHERE STProcedureID  IN  
        (SELECT item FROM E1.fnSplit((SELECT GroupID FROM E1.E_STProcedure WHERE STProcedureID='||OWNER||'),'_'))
    

    其中||OWNER||是个人 STProcedureID E1 是我拥有的数据库架构

    限制


    由于系统是在MTMS多租户多模式)环境中实现的,因此每个查询都依赖于模式并且具有100多个模式,上述代码仅适用于 E1 架构。

    我得出的结论是,在SQL中使用单行查询分割字符串(带分隔符)可能存在一种解决方法,而不使用用户定义的函数,因为唯一的另一个选择是替换并执行查询每个模式及其标记。

2 个答案:

答案 0 :(得分:1)

好吧,为了争论,让我们暂时跳过CTE,而不是做长而丑陋的&#34;用一些xml。我将在这里留下你的一行休息。这个基础知识是从Brad Shulz通过Aaron Bertrand提取的。我从一个int列表改变了,缩短了一点,把它从一个函数中取出来,然后把它放在一行上。

SELECT * FROM E_STProcedure WHERE STProcedureID  IN  
    (SELECT Item FROM (SELECT Item = b.i.value('(./text())[1]', 'VARCHAR(MAX)') FROM (SELECT x = CONVERT(XML, '<i>' + REPLACE(Group_ID, '_', '</i><i>') + '</i>').query('.') FROM E_STProcedure WHERE STProcedureID='||OWNER||') a CROSS APPLY x.nodes('i') b(i)) c WHERE Item IS NOT NULL)

考虑到一些换行符,它会更好看:

SELECT * 
  FROM E_STProcedure 
 WHERE STProcedureID IN (
       SELECT Item 
       FROM (SELECT Item = b.i.value('(./text())[1]', 'VARCHAR(MAX)') 
               FROM (SELECT x = CONVERT(XML, '<i>' + 
                                              REPLACE(Group_ID, '_', '</i><i>') +
                                             '</i>').query('.') 
                       FROM E_STProcedure 
                      WHERE STProcedureID='||OWNER||') a 
                    CROSS APPLY 
                    x.nodes('i') b(i)
                    ) c 
               WHERE Item IS NOT NULL)

仍然可以作为CTE完成,但我没有看到收益。

答案 1 :(得分:0)

我能够在同事的帮助下制定以下解决方案

SELECT * FROM E1.E_STProcedure WHERE STProcedureID  IN  
    (SELECT ST2.STProcedureID   FROM E1.E_STProcedure ST1 
                                JOIN E1.E_STProcedure ST2 ON ST1.GroupID LIKE '%'+ST2.STProcedureID+'%' 
                                WHERE ST1.STProcedureID ='||OWNER||')

The Final Result


解释

WHERE子句

这里发生的是WHERE子句肯定会将结果从ST1相对于相应的GUID ||OWNER||减少到单个记录。 让我们通过以下示例进行简化

WHERE ST1.STProcedureID ='17bfa492-8464-423f-a5f4-32978186e112'

The result of the WHERE clause

JOIN条款

JOIN语句显示果汁,检查来自ST1.GroupID的单个条目是否包含来自ST2.STProcedureID的子字符串,如下所示

E1.E_STProcedure ST1 
JOIN 
E1.E_STProcedure ST2 
ON 
ST1.GroupID LIKE '%'+ST2.STProcedureID+'%'

The result of the JOIN clause


我希望代码对你们所有人来说都是可读的。