SQL Dynamic Charindex - PullRequest
       28

SQL Dynamic Charindex

0 голосов
/ 05 марта 2019

У меня есть поле в таблице sql, но мне нужно разобрать его с помощью charindex, но предостережение lttle: я не знаю, сколько там частей.

Данные поля будут выглядетькак следующее:

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

Но вопрос, я не уверен, сколько имен файлов будет в этой строке, поэтому мне нужно динамически построить это, это может быть 1 или это может быть 100.

Есть предложения?

Спасибо

Ответы [ 3 ]

0 голосов
/ 05 марта 2019

Реальная проблема: это ломается 1.NF .Никогда не следует хранить более одной части данных в одной ячейке.Такие CSV-форматы - это боль в шее, и вам действительно следует использовать соответствующий столик для хранения подсказок с изображениями один за другим .

Тем не менее, это может быть обработано:

- A таблица макетов

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  |
+----+-------+----------+-------------+---------+
0 голосов
/ 06 марта 2019

Я использовал функцию табличного значения

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,'(')

, а затем просто подставив значение в итоговую таблицу

0 голосов
/ 05 марта 2019

Поскольку вы не можете заранее знать, сколько значений вы извлечете из каждого значения, я бы предложил представлять результаты в виде записей , а не столбцов .

Если вы используете SQL Server 2016 или выше, вы можете использовать функцию STRING_SPLIT(), чтобы превратить детали CSV в записи.Затем SUBSTRING() и CHARINDEX() могут быть использованы для извлечения соответствующей информации:

объявление таблицы @t ([txt] varchar (200)) вставка в @t VALUES ('(Image: "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 , ',') 

Демонстрация на скрипте БД :

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           

Примечание: предполагается, что извлекаемое значение всегда располагается после первого знака равенства и до 2 символов до конца строки.Если шаблон отличается, возможно, вам придется адаптировать вызовы SUBSTRING() / CHARINDEX().

...