简体   繁体   中英

How to store string data into a single row in SQL server 2016

String = 'DOB; 04 March 1999; Passport; ABC123'

I have some data present in the above format. I want to save it in a SQL table in a single row. But only the date of birth and passport number present on second and fourth position. Sometimes data can be changed like this:

String = 'DOB; March 04; Passport; 12345'

OR

String = 'DOB; March 04'

OR

String = 'Passport; ABC123'

What should be my approach? I have to do this using only SQL functions or queries.

I have tried different things like Stringsplit, cursor, parsename, substring, charindex etc but can't get the required result I need.

DECLARE @string2 varchar(max) = 'DOB;Mar 1199;Passport;W123333';
DECLARE @sep char(1) = ';'
    , @dot CHAR(1) = '.';
DECLARE @employee TABLE (
    id INT IDENTITY(1,1) PRIMARY KEY, 
    Dob1 varchar(20), 
    DobNum VARCHAR(20),
    Pass1 VARCHAR(20),
    PassNum VARCHAR(20)
);

Thanks in advance...

EDIT @Dale:

DECLARE @string3 varchar(max) = 'DOB; MARCH 1999; Passport; ABC123';
DECLARE @sep3 char(1) = ';'
DECLARE @dot3 char(1) = '.'
DECLARE @tab3 TABLE(
    id INT IDENTITY(1,1) PRIMARY KEY,
    Dob1 varchar(max),
    Dobnum varchar(max),
    Pass1 varchar(max),
    Passnum varchar(max)
);

WITH QUERY1 as
(
    SELECT REPLACE(@string3, @sep3, @dot3) as ABClist
)
INSERT into @tab3 (DobNum,PassNum)
SELECT PARSENAME (ABCList,3),
        
        PARSENAME (ABCList,1)
        
FROM QUERY1

--TEST
select * from @tab3
select Dobnum, Passnum from @tab3


--cursor
declare 

@Dobnum2 varchar(max),

@Passnum2 varchar(max);

declare cursor3 CURSOR for

select Dobnum,Passnum from @tab3

OPEN cursor3;
FETCH next from cursor3 into
  @Dobnum2, @Passnum2

WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT  @Dobnum2 + @Passnum2

INSERT INTO [dbo].[EMPLOYEE]    
    (dobnum,passnum
    )
    values
    (  @Dobnum2, @Passnum2)

FETCH next from cursor3 into
  @Dobnum2, @Passnum2

END;
Close cursor3;
Deallocate cursor3;

The complicated part of your scenario is that the ordinal positions of the DOB and Passport substrings are not always at second and forth position. But, a JSON-based approach is a possible option here. You need to transform the input string into a valid JSON object with a specific structure ( DOB;Mar 1199;Passport;W123333 into {"DOB":"Mar 1199","Passport":"W123333"} ) and parse the generated JSON using OPENJSON() and explicit schema:

DECLARE @string varchar(max) = 'DOB;Mar 1199;Passport;W123333';

INSERT INTO @employee (DobNum, PassNum)
SELECT DobNum, PassNum
FROM OPENJSON (CONCAT(
   '{"', 
   REPLACE(
      REPLACE(
         REPLACE(
            @String, 
            'DOB;', 
            'DOB":"'
         ), 
         'Passport;', 
         'Passport":"'
      ), 
      ';', 
      '","'
   ), 
   '"}'
   )
) WITH (
   DobNum varchar(20) '$.DOB',
   PassNum varchar(20) '$.Passport'
) j

Use STRING_SPLIT and dynamic pivot

create table employee (
    id INT IDENTITY(1,1) PRIMARY KEY, 
    Dob1 varchar(20), 
    DobNum VARCHAR(20),
    Pass1 VARCHAR(20),
    PassNum VARCHAR(20)
);

create table temp(col varchar(20))
insert into temp 
select * FROM STRING_SPLIT('DOB; 04 March 1999; Passport; ABC123',';')

DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX);

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.col) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'),1,1,'')

set @query = 'insert into employee SELECT ' + @cols + ' from 
            (
                select col
                from temp
           ) x
            pivot 
            (
                 max(col)
                for col in (' + @cols + ')
            ) p '

execute(@query)

Result

Just to throw a different idea out there as well. One method would be to use a string splitter that provides the ordinal position (such as the JSON splitter I use here), to get the values into column name/column value pairs, and then use conditional aggregation to normalise, and INSERT :

WITH CTE AS(
    SELECT MAX(CASE WHEN DSJ.[Position] % 2 = 0 THEN TRIM(DSJ.Item) END) AS ColumnName,
           MAX(CASE WHEN DSJ.[Position] % 2 = 1 THEN TRIM(DSJ.Item) END) AS ColumnValue
    FROM fn.DelimitedSplitJSON(@String,';') DSJ
    GROUP BY DSJ.[Position] / 2)
INSERT INTO @employee (DobNum, PassNum)
SELECT MAX(CASE ColumnName WHEN 'DOB' THEN ColumnValue END),
       MAX(CASE ColumnName WHEN 'Passport' THEN ColumnValue END)
FROM CTE;

JSON splitter definition:

CREATE FUNCTION [fn].[DelimitedSplitNJSON] (@String nvarchar(MAX), @Delimiter nvarchar(10))
RETURNS TABLE
AS RETURN
    SELECT [value] AS Item,
           [key] AS Position
    FROM OPENJSON(N'["' + REPLACE(@String,@Delimiter,N'","') + N'"]"');

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM