SQL动态Charindex

时间:2019-03-04 22:57:59

标签: sql-server tsql sql-server-2012 substring charindex

我在sql表中有一个字段,但是我需要通过charindex来解析它,但是需要注意的是,我不知道有多少个字段。

字段数据如下所示:

(Image: "filename=a.jpg"), (Image: "filename=b.jpg") 

但是问题是我不确定该字符串中将有多少个文件名,因此我需要动态地构建此文件名,该文件名可以是1或100。

有什么建议吗?

谢谢

3 个答案:

答案 0 :(得分:0)

由于您无法预先知道将从每个值中提取多少个值,因此建议将结果表示为记录,而不是

如果您使用的是SQL Server 2016或更高版本,则可以使用功能STRING_SPLIT()将CSV部分转换为记录。然后,SUBSTRING()CHARINDEX()可用于提取相关信息:

声明@t表([txt] varchar(200)) 插入@t VALUES('(图像:“ filename = a.jpg”),(图像:“ filename = b.jpg”)')

SELECT value, SUBSTRING(
    value, 
    CHARINDEX('=', value) + 1, 
    LEN(value) - CHARINDEX('=', value) - 2
)
FROM @t t 
CROSS APPLY STRING_SPLIT(t.txt , ',') 

Demo on DB Fiddle

DECLARE @t table ([txt] varchar(200))
INSERT INTO @t VALUES ('(Image: "filename=a.jpg"),(Image: "filename=b.jpg")')

SELECT value, SUBSTRING(
    value, 
    CHARINDEX('=', value) + 1, 
    LEN(value) - CHARINDEX('=', value) - 2
)
FROM @t t 
CROSS APPLY STRING_SPLIT(t.txt , ',') 
GO
value                     | (No column name)
:------------------------ | :---------------
(Image: "filename=a.jpg") | a.jpg           
(Image: "filename=b.jpg") | b.jpg           

NB:这假定要提取的值始终位于第一个等号之后,直到字符串末尾的2个字符为止。如果模式不同,则可能需要调整SUBSTRING() / CHARINDEX()调用。

答案 1 :(得分:0)

真正的问题是:这破坏了 1.NF 。您永远不要在一个单元中存储多个数据。这样的CSV格式令人不寒而栗,您确实应该使用相关的边桌来一个存储图像提示。

尽管如此,这可以解决:

-一个样机表

DECLARE @mockup TABLE(ID INT IDENTITY,YourString VARCHAR(1000));
INSERT INTO @mockup VALUES
 ('(Image: "filename=a.jpg"), (Image: "filename=b.jpg") ')
,('(Image: "filename=aa.jpg"), (Image: "filename=bb.jpg"), (Image: "filename=cc.jpg"), (Image: "filename=dd.jpg"), (Image: "filename=ee.jpg")');

-通过其位置选择一个元素:

DECLARE @position INT=2;

SELECT CAST('<x>' + REPLACE(t.YourString,',','</x><x>') + '</x>' AS XML)
       .value('/x[position()=sql:variable("@position")][1]','nvarchar(max)')
FROM @mockup t;

诀窍是,将字符串转换为XML,然后使用XQuery通过位置获取所需的元素。中间XML如下所示:

<x>(Image: "filename=a.jpg")</x>
<x> (Image: "filename=b.jpg") </x>

您可以使用更多替代品和L/RTRIM()使其更清洁。

读取表数据

如果您想创建一个干净的边桌,并且需要将所有数据整齐地分开,则可以使用更多相同的东西

SELECT CAST('<x><y><z>' 
           + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
                    t.YourString,'(','') --no opening paranthesis
                                ,')','') --no closing paranthesis
                                ,'"','') --no quotes
                                ,' ','') --no blanks
                                ,'=','</z><z>')                --Split at "="
                                ,':','</z></y><y><z>')         --Split at ":"
                                ,',','</z></y></x><x><y><z>')  --Split at ","
                    + '</z></y></x>' AS XML)
FROM @mockup t;

这将返回

<x>
  <y>
    <z>Image</z>
  </y>
  <y>
    <z>filename</z>
    <z>a.jpg</z>
  </y>
</x>
<x>
  <y>
    <z>Image</z>
  </y>
  <y>
    <z>filename</z>
    <z>b.jpg</z>
  </y>
</x>

这样,您将获得一个干净的EAV表(

WITH Casted AS
(
    SELECT ID
          ,CAST('<x><y><z>' 
               + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
                        t.YourString,'(','')
                                    ,')','')
                                    ,'"','')
                                    ,' ','')
                                    ,'=','</z><z>')
                                    ,':','</z></y><y><z>')
                                    ,',','</z></y></x><x><y><z>') 
                        + '</z></y></x>' AS XML) AS CastedToXml
    FROM @mockup t
)
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ID
      ,ID AS oldId
      ,eachElement.value('y[1]/z[1]','varchar(max)') AS DataType
      ,eachElement.value('y[2]/z[1]','varchar(max)') AS ContentType
      ,eachElement.value('y[2]/z[2]','varchar(max)') AS Content
FROM Casted
CROSS APPLY CastedToXml.nodes('/x') A(eachElement)

结果

+----+-------+----------+-------------+---------+
| ID | oldId | DataType | ContentType | Content |
+----+-------+----------+-------------+---------+
| 1  | 1     | Image    | filename    | a.jpg   |
+----+-------+----------+-------------+---------+
| 2  | 1     | Image    | filename    | b.jpg   |
+----+-------+----------+-------------+---------+
| 3  | 2     | Image    | filename    | aa.jpg  |
+----+-------+----------+-------------+---------+
| 4  | 2     | Image    | filename    | bb.jpg  |
+----+-------+----------+-------------+---------+
| 5  | 2     | Image    | filename    | cc.jpg  |
+----+-------+----------+-------------+---------+
| 6  | 2     | Image    | filename    | dd.jpg  |
+----+-------+----------+-------------+---------+
| 7  | 2     | Image    | filename    | ee.jpg  |
+----+-------+----------+-------------+---------+

答案 2 :(得分:0)

我使用了表值函数

ALTER FUNCTION [dbo].[Fn_sqllist_to_table](@list  AS VARCHAR(8000),
                                           @delim AS VARCHAR(10))
RETURNS @listTable TABLE(
  Position INT,
  Value    VARCHAR(8000))
AS
  BEGIN
      DECLARE @myPos INT

      SET @myPos = 1

      WHILE Charindex(@delim, @list) > 0
        BEGIN
            INSERT INTO @listTable
                        (Position,Value)
            VALUES     (@myPos,LEFT(@list, Charindex(@delim, @list) - 1))

            SET @myPos = @myPos + 1

            IF Charindex(@delim, @list) = Len(@list)
              INSERT INTO @listTable
                          (Position,Value)
              VALUES     (@myPos,'')

            SET @list = RIGHT(@list, Len(@list) - Charindex(@delim, @list))
        END

      IF Len(@list) > 0
        INSERT INTO @listTable
                    (Position,Value)
        VALUES     (@myPos,@list)

      RETURN
  END 

通过

调用
select * into #test from tableX as T
cross apply [Fn_sqllist_to_table](fieldname,'(')

然后将值细分为最终表