在SQL查询期间或在服务器端解析结果文本

时间:2014-05-26 07:08:12

标签: sql sql-server sql-function

我给出了如下数据表的示例。 key_value是唯一的,用于获取搜索结果。我目前所做的是使用key_value进行查询,然后解析内容。

+-----+-------------+----------------+
| id  | key_value   | content        |
+-----+-------------+----------------+
| 1   | 001         | 100SW          |
| 2   | 157         | 80SW/20CO      |
| 3   | 222         | 50EA/50CMD     |
| 4   | 275         | 1EA/29LI/70BW  |
+-----+-------------+----------------+

我目前拥有的内容:( key_value的示例:275)

+-----+-------------+----------------+
| id  | key_value   | content        |
+-----+-------------+----------------+
| 4   | 275         | 1EA/29LI/70BW  |
+-----+-------------+----------------+

我想要实现的是这样的。 (key_value的例子:275)

+-------------+----------+----------+
| key_value   | percent  | content  |
+-------------+----------+----------+
| 275         | 1        | EA       |
| 275         | 29       | LI       |
| 275         | 70       | BW       |
+-------------+----------+----------+

是否可以在服务器端实现查询?

P.S。:如果重要的话,目前正在使用SQL Server 2005.

5 个答案:

答案 0 :(得分:2)

我不确定这是否适用于sql server 2005.但它确实适用于sql server 2008.你先做的是一个递归cte,在/字符(with...)上砍掉内容。然后,您可以将结果拆分为数字和字母数字部分。对于你的testdata我已经使用了@t,你可以观察到。将@t替换为with... select...中的表格名称。

declare @t table (id int, key_value varchar(3), content varchar(max))

insert into @t
values (1, '001', '100SW')

insert into @t
values (2, '157', '80SW/20CO')

insert into @t
values (3, '222', '50EA/50CMD')

insert into @t
values (4, '275', '1EA/29LI/70BW')

;with data as (
     select key_value
          , case when CHARINDEX('/', content) > 0 
                 then SUBSTRING(content,1,CHARINDEX('/', content)-1)
                 else content
            end as a
          , case when CHARINDEX('/', content) > 0
                 then substring(content,CHARINDEX('/', content)+2, LEN(content)-CHARINDEX('/', content)) 
                 else content
            end as b
       from @t
     union all
     select key_value
          , case when CHARINDEX('/', b) > 0 
                 then SUBSTRING(b,1,CHARINDEX('/', b)-1)
                 else b
            end as a
          , case when CHARINDEX('/', b) > 0
                 then substring(b,CHARINDEX('/', b)+2, LEN(b)-CHARINDEX('/', b)) 
                 else null
            end as b
       from data
      where b is not null
)
select key_value
     , left(a,PATINDEX('%[a-zA-Z]%',a)-1) as [percent]
     , SUBSTRING(a,PATINDEX('%[a-zA-Z]%',a), LEN(a)-PATINDEX('%[a-zA-Z]%',a)+1) as content
  from data

答案 1 :(得分:1)

请检查一下......

declare @t table(id int, key_value varchar(50), content varchar(100))

insert into @t values (1 ,'001', '100SW'),(2,'157', '80SW/20CO' ),( 3 ,'222','50EA/50CMD'),(4,'275','1EA/29LI/70BW')

declare @idTofind int = 275

select b.key_value , b.FirstName as name
 from  (select * from 
(
    SELECT distinct  key_value,  S.a.value('(/H/r)[1]', 'VARCHAR(100)') FirstName
     , S.a.value('(/H/r)[2]', 'VARCHAR(100)') SecondName, S.a.value('(/H/r)[3]', 'VARCHAR(100)') ThirdName
     FROM 
     (
        SELECT *,CAST (N'<H><r>' + Replace( content, '/','</r><r>') + '</r></H>' AS XML) AS [vals]
        FROM @t
     ) d CROSS APPLY d.[vals].nodes('/H/r') S(a)
) a where key_value = @idTofind
)b
union 
select b.key_value , b.SecondName as name
 from  (select * from 
(
    SELECT distinct  key_value,  S.a.value('(/H/r)[1]', 'VARCHAR(100)') FirstName
     , S.a.value('(/H/r)[2]', 'VARCHAR(100)') SecondName, S.a.value('(/H/r)[3]', 'VARCHAR(100)') ThirdName
     FROM 
     (
        SELECT *,CAST (N'<H><r>' + Replace( content, '/','</r><r>') + '</r></H>' AS XML) AS [vals]
        FROM @t
     ) d CROSS APPLY d.[vals].nodes('/H/r') S(a)
) a where key_value = @idTofind
)b
union 
select b.key_value , b.ThirdName as name
 from  (select * from 
(
    SELECT distinct  key_value,  S.a.value('(/H/r)[1]', 'VARCHAR(100)') FirstName
     , S.a.value('(/H/r)[2]', 'VARCHAR(100)') SecondName, S.a.value('(/H/r)[3]', 'VARCHAR(100)') ThirdName
     FROM 
     (
        SELECT *,CAST (N'<H><r>' + Replace( content, '/','</r><r>') + '</r></H>' AS XML) AS [vals]
        FROM @t
     ) d CROSS APPLY d.[vals].nodes('/H/r') S(a)
) a where key_value = @idTofind
)b

答案 2 :(得分:1)

首先你可以创建功能

CREATE FUNCTION [dbo].func_split_string
(
    @input as varchar(max)
)
RETURNS @result TABLE
(
    content VARCHAR(20),
    [percent] VARCHAR(20)
)
AS
BEGIN

 DECLARE @name VARCHAR(255)
 DECLARE @content VARCHAR(20)
 DECLARE @percent VARCHAR(20)
 DECLARE @pos INT
 SET @input = @input + '/'
    WHILE CHARINDEX('/', @input) > 0
    BEGIN
      SELECT @pos  = CHARINDEX('/', @input)
      SELECT @name = SUBSTRING(@input, 1, @pos-1)


  SELECT @percent = LEFT (@name, PATINDEX('%[a-zA-Z]%', @name)-1)
  SELECT @content = RIGHT (@name, LEN(@name)-PATINDEX('%[a-zA-Z]%', @name)+1)

      INSERT INTO @result ([percent], content)
      SELECT @percent, @content

      SELECT @input = SUBSTRING(@input, @pos+1, LEN(@input)-@pos)

    END
    RETURN
END 

然后你可以像这样运行它

SELECT t.key_value, fS.[percent], fS.content
FROM yourTableHere as t
CROSS APPLY [dbo].func_split_string(t.[content]) as fS

答案 3 :(得分:1)

类似于Brett Schneider的答案(在我写完这个查询后读到的,我应该事先检查过)。

WITH Contents AS (
  SELECT key_value
       , value = CASE WHEN CharIndex('/', content) > 0
                      THEN SubString(content, 1, CharIndex('/', content) - 1)
                      ELSE content
                 END
       , next_value 
       = CASE WHEN CharIndex('/', content) > 0
              THEN SubString(content, CharIndex('/', content) + 1
                           , Len(content))
              ELSE ''
         END
  FROM   table1
  UNION ALL
  SELECT t1.key_value
       , value 
       = CASE WHEN CharIndex('/', c.next_value) > 0
              THEN SubString(c.next_value, 1, CharIndex('/', c.next_value) - 1)
              ELSE c.next_value
         END
       , next_value 
       = CASE WHEN CharIndex('/', c.next_value) > 0
              THEN SubString(c.next_value, CharIndex('/', c.next_value) + 1
                           , Len(c.next_value))
              ELSE ''
         END
  FROM   table1 t1
         INNER JOIN Contents c ON t1.key_value = c.key_value
  WHERE  c.next_value <> ''
), FindNumber AS (
  SELECT key_value
       , CharPos = CASE WHEN IsNumeric(SubString(value, 3, 1)) = 1 THEN 4
                        WHEN IsNumeric(SubString(value, 2, 1)) = 1 THEN 3
                        ELSE 2
                   END
       , value
  FROM   Contents
)
SELECT Key_Value
     , [Percent] = Cast(SubString(value, 1, CharPos - 1) AS int)
     , Content = SubString(value, CharPos, Len(value))
FROM   FindNumber

SQLFiddle demo

最大的区别是我们从字符串中分割数字的方式,在我的脚本中,我检查第三个字符,然后检查第二个字符是否是数字,因为数字是百分比,将永远不会超过3个数字,并且百分比是整数而不是字符串。


OP的评论似乎表明了对性能的追求,在这种情况下值得注意的是,有时,我想强调 SOMETIMES 部分,翻译{{ 1}}一些数学结构将加快执行速度。通常的权衡是可读性 没有CASE的上一个查询是

CASE

SQLFiddle demo

工作原理:

    如果字符&#39; /&#39;
  • WITH Contents AS ( SELECT key_value , value = SubString(content, 1 , Cast(CharIndex('/', content) as bit) * (CharIndex('/', content) - 1) + (1 - Cast(CharIndex('/', content) as bit)) * Len(content)) , next_value = SubString(content , Cast(CharIndex('/', content) as bit) * (CharIndex('/', content)) + (1 - Cast(CharIndex('/', content) as bit)) * Len(content) + 1, Len(content)) FROM table1 UNION ALL SELECT t1.key_value , value = SubString(c.next_value, 1 , Cast(CharIndex('/', c.next_value) as bit) * (CharIndex('/', c.next_value) - 1) + (1 - Cast(CharIndex('/', c.next_value) as bit)) * Len(c.next_value)) , next_value = SubString(c.next_value , Cast(CharIndex('/', c.next_value) as bit) * (CharIndex('/', c.next_value)) + (1 - Cast(CharIndex('/', c.next_value) as bit)) * Len(c.next_value) + 1, Len(c.next_value)) FROM table1 t1 INNER JOIN Contents c ON t1.key_value = c.key_value WHERE c.next_value <> '' ), FindNumber AS ( SELECT key_value , CharPos = 2 + IsNumeric(SubString(value, 3, 1)) + IsNumeric(SubString(value, 2, 1)) , value FROM Contents ) SELECT * FROM FindNumber 为1在内容中找到
  • 如果&#39; /&#39;
  • Cast(CharIndex('/', content) as bit)为1找不到

表示公式

(1 - Cast(CharIndex('/', content) as bit))
伪代码中的

  Cast(CharIndex('/', content) as bit) * (CharIndex('/', content) - 1)
+ (1 - Cast(CharIndex('/', content) as bit)) * Len(content))

因此,如果找到该字符,则公式返回 (IsFound) * (CharIndex('/', content) - 1) + (Not IsFound) * Len(content) ,否则返回CharIndex('/', content) - 1

Len(content)返回一个int:1如果参数是数字,则返回0;如果参数不是,则返回0。

答案 4 :(得分:1)

要将字符串分隔为我们使用nodes() Method of XML data type的元素。要使用它,我们应该将content字段转换为XML格式。只需将/替换为</x><x>即可。然后使用PATINDEX()函数查找字符串中的最后一位数字和第一个字母(%[0-9][a-z,A-Z]%模式),我们将其分为数字和单词。

WITH CTE as 
(
select id,key_value,
       CAST('<x>'+REPLACE(content,'/','</x><x>')+'</x>' as XML) as xmlcont
FROM T
), CTE2 as 
(
 SELECT CTE.*,
       a.f.value('data(.)','varchar(1000)') as xmlval
       FROM CTE
  CROSS APPLY CTE.xmlcont.nodes('x') as a(f)
)
  SELECT ID,KEY_VALUE,
         LEFT(XMLval,PATINDEX('%[0-9][a-z,A-Z]%',XMLval)) as [PERCENT],
         SUBSTRING(XMLval,PATINDEX('%[0-9][a-z,A-Z]%',XMLval)+1,1000) as CONTENT
   FROM CTE2

SQLFiddle demo