簡體   English   中英

在T-SQL中將單個字段值拆分為多個固定長度的列值

[英]Split A Single Field Value Into Multiple Fixed-Length Column Values in T-SQL

我在SO上看了大約15個不同的答案,但還沒有找到這個確切的情況。 我正在進行自定義數據導出,需要導出到一個數據文件,該文件將導入到需要特定長度/格式數據的舊系統中。 我有一個“備忘錄”欄,里面可以包含大量文字。 我需要選擇該值並將其拆分為多個列,其中FIXED長度為75個字符。 例如,如果我有一行帶有185個字符的消息,我需要將其分成3個新的75個字符列,MEMO1,MEMO2,MEMO3,其中MEMO3中的剩余空格填充空格以等於75個字符。 另一個問題是,我最多只能使用18個75-char列來轉儲數據。 如果它長於1350(18x75)個字符,則其余字符將被截斷。

我嘗試了這種方法,但並未考慮新備忘錄列的總數。 我需要一些方法來迭代NUMBEROFMEMOS並且只選擇必要數量的新MEMO列,但顯然你不能在select中做一個WHILE。

SELECT FIRSTNAME, 
       LASTNAME, 
       DOB, 
       CEILING(LEN(NOTETEXT) / 75.0) as NUMBEROFMEMOS,
       SUBSTRING(NOTETEXT, 1, 75) as MEMOLINE1,
       SUBSTRING(NOTETEXT, 76, 149) as MEMOLINE2,
       SUBSTRING(NOTETEXT, 150, 224) as MEMOLINE3,
       etc. etc. etc
FROM CUSTOMER

我是一個長期的應用程序開發人員,他試圖更多地參與數據庫方面的工作。 如果我在C#世界,我只會創建一個方法來執行for循環到NUMBEROFMEMOS並以這種方式輸出數據。 我覺得這不適用於此。 提前致謝!

因為你是.net開發人員,我想你可以很容易地編寫一個可以在你的T-SQL代碼中使用的.net函數。 為了編寫SQL CLR函數,請檢查這個答案 (我使用了其中一個鏈接來實現SQL CLR正則表達式函數。


假設您需要將值拆分為4個長度的塊,並顯示最多6個:

DECLARE @DataSouce TABLE
(
    [RecordID] TINYINT IDENTITY(1,1) PRIMARY KEY
   ,[RecordData] NVARCHAR(MAX)
);

INSERT INTO @DataSouce ([RecordData])
VALUES ('test some test goes here')
      ,('some numbers go here - 1111122222233333344444444445');


SELECT DS.[RecordID]
      ,RM.[MatchID]
      ,RM.[CaptureValue]
FROM @DataSouce DS
CROSS APPLY [dbo].[fn_Utils_RegexMatches] ([RecordData], '.{1,4}') RM;

在此輸入圖像描述

現在數據被拆分了。 讓我們pivot它並僅顯示6個塊:

SELECT *
FROM
(
    SELECT DS.[RecordID]
          ,RM.[MatchID]
          ,RM.[CaptureValue]
    FROM @DataSouce DS
    CROSS APPLY [dbo].[fn_Utils_RegexMatches] ([RecordData], '.{1,4}') RM
) DS
PIVOT
(
    MAX([CaptureValue]) FOR [MatchID] IN ([0], [1], [2], [3], [4], [5], [6])
) PVT;

在此輸入圖像描述

在這里,我使用regex函數來分割數據和PIVOT來創建列並排除一些塊。 您現在可以在表中插入數據以實現它,然后將其導出。 您可以使用上面的鏈接實現這樣的功能,或者創建自己的功能來做你需要的事情。

您可以使用動態SQL。 在這里,您可以使用一個示例來解決您的問題:

declare @text nvarchar(max) = N'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
declare @len int = 75;

declare @NUMBEROFMEMOS int = CEILING(LEN(@text) / @len);

declare @query nvarchar(max) = N'select ';

declare @loop int = 0;
declare @start int = 1;
declare @memoline int = 1;

while @loop <= @NUMBEROFMEMOS begin
    if @loop > 0 begin
        set @query += N', ';
    end

   set @query += N'substring(''' + @text + N''', ' + cast(@start as nvarchar(max)) + N', ' + cast(@len as nvarchar(max)) + N') as MEMOLINE' + cast(@memoline as  nvarchar(max));

   set @start += @len
   set @loop += 1;
   set @memoline += 1;
end

execute sp_sqlexec @query;

這是一個將數據標准化的cte。 從那里你可以轉動

Declare @Customer table (FirstName varchar(50),LastName varchar(50),DOB Date,Memo varchar(max))
Insert into @Customer values 
('John','Doe'  ,'1964-07-29','I''ve looked at about 15 different answers on SO but haven''t found this exact situation yet. I''m doing a custom data export and need to export to a data file that will be imported into an older system that needs the data in a specific length/format. I have a "MEMO" column that can have a large amount of text in it. I need to select that value and split it into multiple columns with a FIXED length of 75 chars. For instance, if I have a row with a message that is 185 chars, I need to split that into 3 new columns of 75 chars, MEMO1, MEMO2, MEMO3, with the remaining space in MEMO3 being filled with spaces to equal the 75 chars. The other catch, I can only use up to 18 75-char columns to dump the data into. If it''s longer than 1350 (18x75) chars, the rest gets truncated.'),
('Jane','Smith','1972-03-21','I''m a long-time application dev who is trying to get more involved in the DB side of things. If I were in the C# world, I would just create a method to do a for loop up to NUMBEROFMEMOS and output the data that way. I don''t think that works here though. Thanks in advance!')

Declare @MaxLen int = 75

;with cteBase as (
    Select FirstName,LastName,DOB,Row=1,Memo=substring(Memo,1,@MaxLen) from @Customer
    Union All
    Select h.FirstName,h.LastName,h.DOB,Row=cteBase.Row+1,Memo=substring(h.Memo,((cteBase.Row+0)*@MaxLen)+1,@MaxLen) FROM @Customer h INNER JOIN cteBase ON h.FirstName = cteBase.FirstName and h.LastName = cteBase.LastName where substring(h.Memo,((cteBase.Row+0)*@MaxLen)+1,@MaxLen)<>''
)
--Select * from cteBase Order by LastName,Row
Select FirstName,LastName,DOB
      ,Memo01=max(case when Row=1 then Memo else null end)
      ,Memo02=max(case when Row=2 then Memo else null end)
      ,Memo03=max(case when Row=3 then Memo else null end)
      ,Memo04=max(case when Row=4 then Memo else null end)
      ,Memo05=max(case when Row=5 then Memo else null end)
      ,Memo06=max(case when Row=6 then Memo else null end)
      ,Memo07=max(case when Row=7 then Memo else null end)
      ,Memo08=max(case when Row=8 then Memo else null end)
      ,Memo09=max(case when Row=9 then Memo else null end)
      ,Memo10=max(case when Row=10 then Memo else null end)
      ,Memo11=max(case when Row=11 then Memo else null end)
      ,Memo12=max(case when Row=12 then Memo else null end)
      ,Memo13=max(case when Row=13 then Memo else null end)
      ,Memo14=max(case when Row=14 then Memo else null end)
      ,Memo15=max(case when Row=15 then Memo else null end)
      ,Memo16=max(case when Row=16 then Memo else null end)
      ,Memo17=max(case when Row=17 then Memo else null end)
      ,Memo18=max(case when Row=18 then Memo else null end)
  from cteBase
  Group By FirstName,LastName,DOB
 Order by LastName

CTE返回

FirstName   LastName    DOB         Row Memo
John        Doe         1964-07-29  1   I've looked at about 15 different answers on SO but haven't found this exac
John        Doe         1964-07-29  2   t situation yet. I'm doing a custom data export and need to export to a dat
John        Doe         1964-07-29  3   a file that will be imported into an older system that needs the data in a 
John        Doe         1964-07-29  4   specific length/format. I have a "MEMO" column that can have a large amount
John        Doe         1964-07-29  5    of text in it. I need to select that value and split it into multiple colu
John        Doe         1964-07-29  6   mns with a FIXED length of 75 chars. For instance, if I have a row with a m
John        Doe         1964-07-29  7   essage that is 185 chars, I need to split that into 3 new columns of 75 cha
John        Doe         1964-07-29  8   rs, MEMO1, MEMO2, MEMO3, with the remaining space in MEMO3 being filled wit
John        Doe         1964-07-29  9   h spaces to equal the 75 chars. The other catch, I can only use up to 18 75
John        Doe         1964-07-29  10  -char columns to dump the data into. If it's longer than 1350 (18x75) chars
John        Doe         1964-07-29  11  , the rest gets truncated.
Jane        Smith       1972-03-21  1   I'm a long-time application dev who is trying to get more involved in the D
Jane        Smith       1972-03-21  2   B side of things. If I were in the C# world, I would just create a method t
Jane        Smith       1972-03-21  3   o do a for loop up to NUMBEROFMEMOS and output the data that way. I don't t
Jane        Smith       1972-03-21  4   hink that works here though. Thanks in advance!

確保所有結果字段的長度相同......

DECLARE @NoteText nVARCHAR(1350);
DECLARE @newFieldLength int = 3;  --Yours wull be 75

--GET ORIGINAL TEXT
SET @NoteText = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; --SET FROM YOURS SOURCE TABLE I GUESS

--MAKE SURE LAST ONE PADS TO THE REQUIRED LENGTH SO ALL FIELDS ARE THE SAME LENGTH
DECLARE @mod int = @newFieldLength - ( LEN(@NoteText) % @newFieldLength );
--SELECT @mod;
WHILE @Mod > 0
BEGIN
  SET @NoteText = @NoteText + ' ';
  SET @mod = @mod - 1;
END

DECLARE @NoOfFields INT;
SELECT @NoOfFields = CEILING(LEN(@NoteText) / @newFieldLength ) + 1;

DECLARE @Loop INT = 0;
DECLARE @dynSQL nVarchar(MAX) = 'SELECT FIRSTNAME, LASTNAME, DOB, ' + CONVERT(nvarchar(4), @NoOfFields) + ' as NUMBEROFMEMOS, '; 
DECLARE @pos INT = 1;
WHILE @Loop < @NoOfFields
BEGIN  
  IF @Loop > 0
  BEGIN
    SET @Pos = (@Loop * @newFieldLength) + 1;
  END;
  SET @dynSQL = @dynSQL + 'SUBSTRING(@NoteText, ' + CONVERT(nvarchar(2), @pos) + ', '  + CONVERT(nvarchar(2), @newFieldLength) +  ') as MEMOLINE_' + CONVERT(nvarchar(2), @loop + 1) + ', ';

  SET @Loop = @Loop + 1;
END

SET @dynSQL = @dynSQL + 'FROM CUSTOMER';
SET @dynSQL = REPLACE( @dynSQL, ', FROM CUSTOMER', ' FROM CUSTOMER ');

--RUN TEH RESULTANT SQL
EXEC @dynSQL

如果將每行數據划分為不同數量的列,則需要為每一行創建一個INSERT語句。
相反,您始終可以生成所有18個備忘錄列並進行批量插入

INSERT INTO [OtherServer.OtherDB.user.table]
SELECT FIRSTNAME, 
       LASTNAME, 
       DOB, 
       LEFT(SUBSTRING(NOTETEXT, 1, 75) + SPACE(75), 75) as MEMOLINE1,
       LEFT(SUBSTRING(NOTETEXT, 76, 150) + SPACE(75), 75) as MEMOLINE2,
       LEFT(SUBSTRING(NOTETEXT, 151, 225) + SPACE(75), 75) as MEMOLINE3,
       ...
       LEFT(SUBSTRING(NOTETEXT, 1276, 1350) + SPACE(75), 75) as MEMOLINE18,
FROM [myServer.myDB.myUser.CUSTOMER]

如果有很多行要導出,你可以在塊中工作

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM