2012-05-02 17 views
1

我搜索了很多地方,很少使用論壇張貼...所以我相信我有一個罕見的問題在這裏。我有一個包含一對多關係的csv文件。例如,前10個字段包含一條記錄。這繼續到一個空白字段,然後在每個附加字段中的另一個分隔符是一個管道,其中有字段。這實際上是與單個呼叫有關的事件的日誌文件。一對多CSV文件移動到SQL使用邏輯

例如。

ID; MACHINEID; ANI ;; COMMAND | ARGUMENTS | 20120112.06:15:32

加粗的文本可以重複多次或幾次。這是SQL連接的許多方面。我需要回到SQL。我在想SSIS腳本塊。我在哪裏循環,並添加到許多桌子上,一邊是身份證。用管道分隔內部字段作爲分隔符。我只是想知道是否有人曾經見過這個,並有一個簡單的解決方案,也許我不知道。我會想象,這將在XML中創建得更好,但可惜情況並非如此。

任何幫助表示讚賞。如果它主要是建設性的,我甚至會接受批評。非常感謝提前。

要顯示錶化妝

CREATE TABLE [dbo].[tblIVRCalls](
    [CALLID] [char](50) NOT NULL, 
    [MACHINEID] [char](50) NOT NULL, 
    [CHANNEL] [char](50) NOT NULL, 
    [DNIS] [char](50) NOT NULL, 
    [ANI] [char](50) NOT NULL, 
    [STARTTIME] [smalldatetime] NOT NULL, 
    [TRUNK] [char](50) NOT NULL, 
    [APPLICATION] [char](50) NULL, 
    [PLANID] [char](50) NULL, 
    [DERIVEDID] [char](50) NULL, 
    [TOTALTIME] [smalldatetime] NOT NULL, 
CONSTRAINT [PK_tblIVRCalls] PRIMARY KEY CLUSTERED 

CREATE TABLE [dbo].[IVRCallActions](
    [pk] [bigint] IDENTITY(1,1) NOT NULL, 
    [fkCALLID] [char](50) NOT NULL, 
    [SequenceNumber] [bigint] NOT NULL, 
    [Command] [char](50) NOT NULL, 
    [Arguments] [varchar](max) NOT NULL, 
    [ExecutionTime] [datetime] NOT NULL, 
CONSTRAINT [PK_IVRCallActions] PRIMARY KEY CLUSTERED 
+0

SSIS以行方式工作,所以不要指望用SSIS解決這個問題。它應該用一個簡單的C#程序 – JotaBe

+1

@JotaBE處理:你將如何在C#中以不重複遍歷行的方式實現這一點? – JAQFrost

+0

你的桌子是什麼樣的?對於你的例子,預期的結果是什麼? table1中有1行,table2中有N行?信息總是以三部分爲單位還是也是變量? – billinkc

回答

0

根據在SSIS Design Pattern: Loading Variable-Length Rows找到的信息,我能夠創建下面的過程。 SSIS腳本塊可以完成任何您可以使用C#執行的任何操作,並且可以安裝到SQL Server以設定的時間間隔運行。創建了2個輸出進行處理。你通過這個腳本填充每個緩衝區。所以一個平面文件與一列。 2個輸出和這個腳本之間。

public override void Input0_ProcessInputRow(Input0Buffer Row) 
    { 
     IVRCallsBuffer.AddRow(); 
     string[] splitEntireRow = null; 
     var byteRow = Row.Column0.GetBlobData(0,(int)Row.Column0.Length); 
     var entireRow = System.Text.Encoding.ASCII.GetString(byteRow); 
     splitEntireRow = entireRow.Split(';'); 
     int fieldPosition = 0; 
     string CallID = ""; 
     while (splitEntireRow[fieldPosition] != "") 
     { 
      var splitIVRCall = splitEntireRow[fieldPosition].Split('='); 
      switch (splitIVRCall[0]) 
      { 
       case "CALLID": 
        IVRCallsBuffer.CALLID = splitIVRCall[1]; 
        CallID = splitIVRCall[1]; 
        break; 
       case "MACHINEID": 
        IVRCallsBuffer.MACHINEID = splitIVRCall[1]; 
        break; 
       case "CHANNEL": 
        IVRCallsBuffer.CHANNEL = splitIVRCall[1]; 
        break; 
       case "DNIS": 
        IVRCallsBuffer.DNIS = splitIVRCall[1]; 
        break; 
       case "ANI": 
        IVRCallsBuffer.ANI = splitIVRCall[1]; 
        break; 
       case "STARTTIME": 
        IVRCallsBuffer.STARTTIME = DateTime.ParseExact(splitIVRCall[1], "yyyyMMdd.HH:mm:ss.fff", System.Globalization.CultureInfo.CurrentUICulture); 
        break; 
       case "TRUNK": 
        IVRCallsBuffer.TRUNK = splitIVRCall[1]; 
        break; 
       case "APPLICATION": 
        IVRCallsBuffer.Application = splitIVRCall[1]; 
        break; 
       case "PLANID": 
        IVRCallsBuffer.PLANID = splitIVRCall[1]; 
        break; 
       case "DERIVEDID": 
        IVRCallsBuffer.DERIVEDID = splitIVRCall[1]; 
        break; 
       case "TOTALTIME": 
        IVRCallsBuffer.TOTALTIME = DateTime.ParseExact(splitIVRCall[1],"mm:ss.fff",System.Globalization.CultureInfo.CurrentUICulture); 
        break; 
      } 
      fieldPosition++; 
     } 
     fieldPosition++; 
     uint sequence = 1; 
     while (splitEntireRow.GetUpperBound(0) -1 > fieldPosition) 
     { 
      IVRCallActionsBuffer.AddRow(); 
      var splitIVRCallAction = splitEntireRow[fieldPosition].Split('|'); 
      IVRCallActionsBuffer.fkCALLID = CallID; 
      IVRCallActionsBuffer.SequenceNumber = sequence; 
      IVRCallActionsBuffer.Command = splitIVRCallAction[0]; 
      IVRCallActionsBuffer.Arguments = splitIVRCallAction[1]; 
      IVRCallActionsBuffer.ExecutionTime = DateTime.ParseExact(splitIVRCallAction[2],"yyyyMMdd.HH:mm:ss.fff", System.Globalization.CultureInfo.CurrentUICulture); 
      fieldPosition++; 
      sequence++; 
     } 

    } 
} 
0

一種選擇是將數據導入到一個臨時表,其中,所述重複的元素被存儲爲一個varchar(最大值)(或nvarchar(最大值)爲純粹主義者)。

然後執行下列操作:

  1. 插入非重複字段放入表1
  2. 捕捉剛插入的行的標識,使用標識@@
  3. 遍歷的重複場,第一提取的行(由一些分隔符的定義),然後該行的分量(由下式定義「|」)。
  4. 將每行插入到表2中,並使用(2)中的標識。

我承認步驟(3)不是最容易的事情。但是,如果你寫一個存儲過程,並使用分離功能(http://stackoverflow.com/questions/2647/split-string-in-sql),那麼它是可行的。

根據我的經驗,複雜的字符串操作可以工作到數百兆字節。如果您一次需要處理多個千兆字節,那麼您可能需要自定義代碼。

+0

我知道來自VBA和C#的分割函數,我會想象它是相似的。我會看看你提供的鏈接。你如何在SQL中嵌套循環。我不那麼熟悉。它會是一個像CTE一樣的遞歸查詢嗎?我對CTE並不熟悉,但我想他們聽起來像是符合法案。 – dstigue

0

我建議創建一個控制檯程序(用C#,VBS或任何你可以使用),以從原始文件中得到2個文件。

然後,你可以在SSIS,這是管理的空字段,更改數據類型等沒有很好的處理這兩個文件。

該計劃將只是逐行讀取原始文件行,將處理它獲取父行和相關的兒童行,並寫在一個文件中的父母和其他的孩子們。這些文件將成爲SSIS包的條目。

什麼不該做:

  • 使這種過程是在T-SQL,它有沒有很多功能,允許處理字符串的要困難得多。
  • 在SSIS中做到這一點是不可能的。沒有組件可以分解多行中的一行。更不用說它應該產生兩種不同類型的行。 (一個自定義的SSIS組件可以做到這一點,但這並不值得)
+0

可以通過腳本塊將C#添加到SSIS。 [SSIS設計模式:加載可變長度行](http://sqlblog.com/blogs/andy_leonard/archive/2010/05/18/ssis-design-pattern-loading-variable-length-rows.aspx)顯示很少有這樣做可以做到。我將不得不循環並創建。我希望有一個更簡單的方法,但這似乎是一個複雜的問題。 – dstigue

+0

令人驚訝。這絕對是真的。我無法找到在sibgle腳本組件中創建多個緩衝區輸出的方法。很棒的解決 – JotaBe