2016-09-09 18 views
3

Zeit zu transformieren und zu kombinieren, ich habe einen Datensatz, der wie folgt aufgebaut:Verwendung von SQL-Strings

CREATE TABLE notes (
    date DATE NOT NULL, 
    author VARCHAR(100) NOT NULL, 
    type CHAR NOT NULL, 
    line_number INT NOT NULL, 
    note VARCHAR(4000) NOT NULL 
); 

Einige Beispieldatum:

Date, Author, Type, Line Number, Note 
2015-01-01, Abe, C, 1, First 4000 character string 
2015-01-01, Abe, C, 2, Second 4000 character string 
2015-01-01, Abe, C, 3, Third 4000 character string 
2015-01-01, Bob, C, 1, First 4000 character string 
2015-01-01, Bob, C, 2, Second 1000 character string 
2015-01-01, Cal, C, 1, First 3568 character string 

Diese Daten sind auf eine zu migrierenden neue SQL Server-Struktur, die definiert ist als:

CREATE TABLE notes (
    date DATE NOT NULL, 
    author VARCHAR(100) NOT NULL, 
    type CHAR NOT NULL, 
    note VARCHAR(8000) NOT NULL 
); 

I (solche mit mehr als 8 auf die mehrzeiligen voran möchte 000 Zeichen, wenn sie kombiniert) stellt mit "Datum-Autor - Teil X von Y //" und legen einen Raum zwischen verketteten Strings, so dass die Daten wie enden würde:

Date, Author, Type, Note 
2015-01-01, Abe, C, 2015-01-01 - Abe - Part 1 of 2 // First 4000 character string First 3959 characters of the second 4000 character string 
2015-01-01, Abe, C, 2015-01-01 - Abe - Part 2 of 2 // Remaining 41 characters of the second 4000 character string Third (up to) 4000 character string 
2015-01-01, Bob, C, First 4000 character string Second 1000 character string 
2015-01-01, Cal, C, First 3568 character string 

Ich suche nach Möglichkeiten, diese Transformation zu erreichen. Anfangs hatte ich einen Zwischenschritt, um alle Notenzeichenfolgen, bei denen Datum, Autor und Typ gemeinsam genutzt wurden, aber nicht teilen konnten, zu kombinieren (zusammenzufügen).

+0

Wenn Sie diese Linie kombinieren wollen, warum Sie nicht nur VARCHAR (MAX) auf Notiz verwenden, und setzen alles in einer Linie? Vielleicht ist das eine einfachere Lösung? – EricZ

+0

@EricZ - Einverstanden! Aber ich kann die Struktur nicht neu definieren, ich bin nicht in den oberen DBA-Rängen. ;) – lojkyelo

+0

Wie wiederholbar muss das sein? Es hört sich so an, als wären Sie mit manuellen Zwischenschritten zufrieden, um die Umwandlung fertig zu stellen, da Sie dies nur einmal tun würden. – iamdave

Antwort

1

Okay, also, das war eine kleine Herausforderung, aber ich bin am Ende angekommen. War eine sehr angenehme Ablenkung von meiner normalen Arbeit: D

Der Code geht davon aus, dass Sie nie eine Notiz haben werden, die länger als 72.000 Zeichen insgesamt ist, in der die Logik, die von der Part x in y hinzugefügt wird Präfix geht davon aus, dass x und y einstellige Zahlen sind. Dies könnte leicht behoben werden, indem einzelne Ziffern mit führenden Nullen aufgefüllt werden, was ebenfalls sicherstellen würde, dass die Reihenfolge korrekt ist.

Wenn Sie alles erklärt benötigen, sollten die Kommentare im Code ausreichen:

-- Declare the test data: 
declare @a table ([Date] date 
        ,author varchar(100) 
        ,type char 
        ,line_number int 
        ,note varchar(8000) 
        ,final_line int 
        ,new_lines int 
        ) 
insert into @a values 
('2015-01-01','Abel','C',1,'This is a note that is 100 characters long----------------------------------------------------------' ,null,null) 
,('2015-01-01','Abel','C',2,'This is a note that is 100 characters long----------------------------------------------------------' ,null,null) 
,('2015-01-01','Abel','C',3,'This is a note that is 83 characters long------------------------------------------'     ,null,null) 
,('2015-01-01','Bob' ,'C',1,'This is a note that is 100 characters long----------------------------------------------------------' ,null,null) 
,('2015-01-01','Bob' ,'C',2,'This is a note that is 43 characters long--'               ,null,null) 
,('2015-01-01','Cal' ,'C',1,'This is a note that is 50 characters long---------'             ,null,null) 



--------------------------------------- 
-- Start the actual data processing. -- 
--------------------------------------- 

declare @MaxFieldLen decimal(10,2) = 100 -- Set this to your 8000 characters limit you have. I have used 100 so I didn't have to generate and work with really long text values. 


-- Create Numbers table. This will perform better if created as a permanent table: 
if object_id('tempdb..#Numbers') is not null 
drop table #Numbers 

;with e00(n) as (select 1 union all select 1) 
     ,e02(n) as (select 1 from e00 a, e00 b) 
     ,e04(n) as (select 1 from e02 a, e02 b) 
     ,e08(n) as (select 1 from e04 a, e04 b) 
     ,e16(n) as (select 1 from e08 a, e08 b) 
     ,e32(n) as (select 1 from e16 a, e16 b) 
     ,cte(n) as (select row_number() over (order by n) from e32) 
select n-1 as Number 
into #Numbers 
from cte 
where n <= 1000001 



-- Calculate some useful figures to be used in chopping up the total note. This will need to be done across the table before doing anything else: 
update @a 
set final_line = t.final_line 
    ,new_lines = t.new_lines 
from @a a 
    inner join (select Date 
         ,author 
         ,type 
         ,max(line_number) as final_line  -- We only want the final line from the CTE later on, so we need a way of identifying that the line_number we are working with the last one. 

         -- Calculate the total number of lines that will result from the additional text being added: 
         ,case when sum(len(note)) > @MaxFieldLen                       -- If the Note is long enough to be broken into two lines: 
          then ceiling(                             -- Find the next highest integer value for 
             sum(len(note))                          -- the total length of all the notes 
              /(@MaxFieldLen - len(convert(nvarchar(10), Date, 121) + ' - ' + author + ' - Part x of x //_'))    -- divided by the max note size allowed minus the length of the additional text. 
             ) 
          else 1                               -- Otherwise return 1. 
          end as new_lines 
       from @a 
       group by Date 
         ,author 
         ,type 
       ) t 
     on a.Date = t.Date 
      and a.author = t.author 
      and a.type = t.type 



-- Combine the Notes using a recursive cte: 
;with cte as 
(
    select Date 
      ,author 
      ,type 
      ,line_number 
      ,final_line 
      ,note 
      ,new_lines 
    from @a 
    where line_number = 1 

    union all 

    select a.Date 
      ,a.author 
      ,a.type 
      ,a.line_number 
      ,a.final_line 
      ,c.note + a.note 
      ,a.new_lines 
    from cte c 
     join @a a 
      on c.Date = a.Date 
       and c.author = a.author 
       and c.type = a.type 
       and c.line_number+1 = a.line_number 

) 
select c1.Date 
     ,c1.author 
     ,c1.type 
     ,c2.note 
from cte c1 
    cross apply (select case when c1.new_lines > 1  -- If there is more than one line to be returned, build up the prefix: 
          then convert(nvarchar(10), Date, 121) + ' - ' + author + ' - Part ' + cast(Number+1 as nvarchar(10)) + ' of ' + cast(c1.new_lines as nvarchar(10)) + ' // ' 
            + substring(c1.note -- and then append the next (Max note length - Generated prefix) number of characters in the note: 
               ,1 + Number * (@MaxFieldLen - len(convert(nvarchar(10), Date, 121) + ' - ' + author + ' - Part x of x //_')) 
               ,(@MaxFieldLen - len(convert(nvarchar(10), Date, 121) + ' - ' + author + ' - Part x of x //_'))-1 
               ) 
          else c1.note 
          end as note 
        from #Numbers 
        where Number >= 0 
         and Number < case when c1.new_lines = 1 
              then 1 
              else len(c1.note)/(@MaxFieldLen - len(convert(nvarchar(10), Date, 121) + ' - ' + author + ' - Part x of x //_')) 
              end 
       ) c2 
where line_number = final_line 
order by 1,2,3,4 
+0

Dies ist eine elegante Lösung in Anbetracht des Problems. Kudos. Ich vergleiche es mit der laufenden Arbeit, die ich habe, nicht die gleichen, aber einige ähnliche Elemente - ich werde etwas aufstellen, damit Sie vergleichen können. Vielen Dank! – lojkyelo