Split column based on ascii value

前端 未结 1 1141
离开以前
离开以前 2021-01-28 07:29

I have a text field in a table that contains a large string, each part of the string that i want to separate is split by a little square.

When searchin

相关标签:
1条回答
  • 2021-01-28 07:47

    Hint: You might want to read "update 3" first :-)

    And the most important hint: Do not store data in such a format. If you can change this, you really should...

    Now some different approaches:

    (And there is hint 3 :-) : Next time please tag with the RDBMS including the version. This makes it easier to help you. Less guessing...)

    Try it like this

    --First I mock-up your issue by replacing the separating blanks with the 27. Now we see the rectangles you were talking about.

    DECLARE @YourString VARCHAR(1000)=REPLACE('ABS_ID=1234567 PERSON_ID=1234567 PARTY_ID= ABS_D=123 ABS_T= ABS_TYPE_ID=12345 ABS_ED=123456',' ',CHAR(27));
    SELECT @YourString;
    

    --This is the query (needs v2016+):

    SELECT A.[key] AS Position
          ,LEFT(Fragment,PosEqual-1) AS ValueName
          ,SUBSTRING(Fragment,PosEqual+1,1000) AS ValueContent
    FROM OPENJSON(CONCAT('["',REPLACE(@YourString,CHAR(27),'","'),'"]')) A
    CROSS APPLY(SELECT A.[value] AS Fragment
                      ,CHARINDEX('=',A.[value]) AS PosEqual) B;
    

    The result

    Position    ValueName   ValueContent
    0           ABS_ID      1234567
    1           PERSON_ID   1234567
    2           PARTY_ID    
    3           ABS_D       123
    4           ABS_T   
    5           ABS_TYPE_ID 12345
    6           ABS_ED      123456
    

    The idea in short:

    Better than STRING_SPLIT() is a JSON-hack, as the first is not position safe.

    Using some simple string methods we can transform your separated string in a JSON array. This array we open using OPENJSON(). This method returns the position as key and the fragment as value.

    The APPLY will search for the position of the =.

    The SELECT will use the position to read the parts left and rigth from the =.

    The result is a classical EAV-list.

    UPDATE: If you use a version below v2016...

    the following query is similiar in principles, but uses a XML-hack and works with versions down to v2005:

    SELECT LEFT(C.Fragment,C.PosEqual-1) AS ValueName
          ,SUBSTRING(C.Fragment,C.PosEqual+1,1000) AS ValueContent
    FROM (SELECT CAST('<x>'+REPLACE(@YourString,CHAR(27),'</x><x>')+'</x>' AS XML)) A(CastedToXml)
    CROSS APPLY A.CastedToXml.nodes('/x') B(xmlFragment)
    CROSS APPLY(SELECT B.xmlFragment.value('text()[1]','nvarchar(1000)') AS Fragment
                      ,CHARINDEX('=',B.xmlFragment.value('text()[1]','nvarchar(1000)')) AS PosEqual) C;
    

    UPDATE 2: One more approach

    You might split this in one single go like this:

    SELECT CAST('<x><y>' + REPLACE(REPLACE(@YourString,'=','</y><y>'),CHAR(27),'</y></x><x><y>') + '</y></x>' AS XML);
    

    Or this:

    SELECT CAST('<x name="' + REPLACE(REPLACE(@YourString,'=','">'),CHAR(27),'</x><x name="') + '</x>' AS XML);
    

    The result is this

    <x>
      <y>ABS_ID</y>
      <y>1234567</y>
    </x>
    <x>
      <y>PERSON_ID</y>
      <y>1234567</y>
    </x>
    <x>
      <y>PARTY_ID</y>
      <y />
    </x>
    <x>
      <y>ABS_D</y>
      <y>123</y>
    </x>
    <x>
      <y>ABS_T</y>
      <y />
    </x>
    <x>
      <y>ABS_TYPE_ID</y>
      <y>12345</y>
    </x>
    <x>
      <y>ABS_ED</y>
      <y>123456</y>
    </x>
    

    Or this:

    <x name="ABS_ID">1234567</x>
    <x name="PERSON_ID">1234567</x>
    <x name="PARTY_ID" />
    <x name="ABS_D">123</x>
    <x name="ABS_T" />
    <x name="ABS_TYPE_ID">12345</x>
    <x name="ABS_ED">123456</x>
    

    UPDATE 3 (this should be on top probably :-) )

    This will do the pivoting implicitly:

    SELECT CastedToXml.value('(/x[@name="ABS_ID"]/text())[1]','bigint') AS ABS_ID
          ,CastedToXml.value('(/x[@name="PERSON_ID"]/text())[1]','bigint') AS PERSON_ID
          ,CastedToXml.value('(/x[@name="PARTY_ID"]/text())[1]','bigint') AS PARTY_ID
          ,CastedToXml.value('(/x[@name="ABS_D"]/text())[1]','bigint') AS ABS_D
          ,CastedToXml.value('(/x[@name="ABS_T"]/text())[1]','bigint') AS ABS_T
          ,CastedToXml.value('(/x[@name="ABS_TYPE_ID"]/text())[1]','bigint') AS ABS_TYPE_ID
          ,CastedToXml.value('(/x[@name="ABS_ED"]/text())[1]','bigint') AS ABS_ED
    FROM
    (SELECT CAST('<x name="' + REPLACE(REPLACE(@YourString,'=','">'),CHAR(27),'</x><x name="') + '</x>' AS XML)) A(CastedToXml);
    

    The result

    ABS_ID  PERSON_ID   PARTY_ID    ABS_D   ABS_T   ABS_TYPE_ID ABS_ED
    1234567 1234567     NULL        123     NULL    12345       123456
    
    0 讨论(0)
提交回复
热议问题