2016-10-21 2 views
1

У меня есть несколько папок (шесть или около того) с несколькими файлами .CSV в них. Файлы CSV все в том же формате:Вставка больших CSV-файлов в одну таблицу SQL [SQL Server 2008]

Heading1,Heading2,Heading3 
1,Monday,2.45 
2,Monday,3.765... 

Каждый .CSV имеет одни и те же имена заголовков [же исходные данные для разных месяцев]. Каков наилучший способ импорта этих CSV в SQL Server 2008? На сервере не настроен xpShell [по причинам безопасности, которые я не могу изменить], поэтому любой метод, который использует это (изначально я пытался), не будет работать.

EDIT

CSV-файлы имеют максимум 2 МБ в размере и не содержат запятые (кроме тех, которые необходимы для разделителей).

Любые идеи?

+0

Насколько велики CSV-файлы? Вы могли бы гипотетически использовать Excel, если вам нужно. Обратите внимание, что 'BULK INSERT' не предоставляет истинный синтаксический анализатор CSV: он не поддерживает экранированные кавычки или даже запятые в цитированных значениях (http://stackoverflow.com/questions/12902110/bulk-insert-correctly-quote-csv- file-in-sql-server), например. – Dai

+0

Самый большой CSV-файл будет размером около 2 МБ. Данные не содержат никаких запятых или экранированных кавычек. – fila

+0

В принципе три варианта - сначала посмотрите, работает ли BULK INSERT для ваших файлов, как было предложено.Если нет, вам может понадобиться использовать SSIS или вы можете использовать внешний процесс (например, скрипт powershell) для ввода данных. –

ответ

0

F.e. вы получили имена файлов CSV sample.csv на D:\ диске, с этим внутри:

Heading1,Heading2,Heading3 
1,Monday,2.45 
2,Monday,3.765 

Затем вы можете использовать этот запрос:

DECLARE @str nvarchar(max), 
     @x xml, 
     @head xml, 
     @sql nvarchar(max), 
     @params nvarchar(max) = '@x xml' 

SELECT @str = BulkColumn 
FROM OPENROWSET (BULK N'D:\sample.csv', SINGLE_CLOB) AS a 

SELECT @head = CAST('<row><s>'+REPLACE(SUBSTRING(@str,1,CHARINDEX(CHAR(13)+CHAR(10),@str)-1),',','</s><s>')+'</s></row>' as xml) 

SELECT @x = CAST('<row><s>'+REPLACE(REPLACE(SUBSTRING(@str,CHARINDEX(CHAR(10),@str)+1,LEN(@str)),CHAR(13)+CHAR(10),'</s></row><row><s>'),',','</s><s>')+'</s></row>' as xml) 

SELECT @sql = N' 
SELECT t.c.value(''s[1]'',''int'') '+QUOTENAME(t.c.value('s[1]','nvarchar(max)'))+', 
     t.c.value(''s[2]'',''nvarchar(max)'') '+QUOTENAME(t.c.value('s[2]','nvarchar(max)'))+', 
     t.c.value(''s[3]'',''decimal(15,7)'') '+QUOTENAME(t.c.value('s[3]','nvarchar(max)'))+' 
FROM @x.nodes(''/row'') as t(c)' 
FROM @head.nodes('/row') as t(c) 

Чтобы получить выход, как:

Heading1 Heading2 Heading3 
1   Monday  2.4500000 
2   Monday  3.7650000 

Сначала мы принимать данные как SINGLE_CLOB с помощью OPEROWSET.

Затем мы помещаем все в переменную @str. Часть от начала до первой \r\n мы помещаем в @head, другая часть в @x с преобразованием в XML. Состав:

<row> 
    <s>Heading1</s> 
    <s>Heading2</s> 
    <s>Heading3</s> 
</row> 

<row> 
    <s>1</s> 
    <s>Monday</s> 
    <s>2.45</s> 
</row> 
<row> 
    <s>2</s> 
    <s>Monday</s> 
    <s>3.765</s> 
</row> 

После того, что мы строим динамический запрос типа:

SELECT t.c.value('s[1]','int') [Heading1], 
     t.c.value('s[2]','nvarchar(max)') [Heading2], 
     t.c.value('s[3]','decimal(15,7)') [Heading3] 
FROM @x.nodes('/row') as t(c) 

И выполнить его. Переменная @x передается как параметр.

Надеюсь, это вам поможет.

0

В итоге я решил решить проблему, используя не-SQL-ответ. Спасибо всем, кто помог внести свой вклад. Приносим извинения за то, что вы используете полностью вне поля ответ с помощью PHP. Вот то, что я создал, чтобы решить эту проблему:

<?php 
    ////////////////////////////////////////////////////////////////////////////////////////////////// 
    //                        // 
    //  Date:   21/10/2016.                // 
    //  Description: Insert CSV rows into pre-created SQL table with same column structure. // 
    //  Notes:   - PHP script needs server to execute.         // 
    //      - Can run line by line ('INSERT') or bulk ('BULK INSERT').    // 
    //       - 'Bulk Insert' needs bulk insert user permissions.     // 
    //                        // 
    //  Currently only works under the following file structure:        // 
    //   | ROOT FOLDER                  // 
    //      | FOLDER 1               // 
    //        | CSV 1              // 
    //        | CSV 2...             // 
    //      | FOLDER 2               // 
    //        | CSV 1              // 
    //        | CSV 2...             // 
    //      | FOLDER 3...               // 
    //        | CSV 1              // 
    //        | CSV 2...             // 
    //                        // 
    ////////////////////////////////////////////////////////////////////////////////////////////////// 

    //Error log - must have folder pre-created to work 
    ini_set("error_log", "phplog/bulkinsertCSV.php.log"); 

    //Set the name of the root directory here (Where the folder's of CSVs are) 
    $rootPath = '\\\networkserver\folder\rootfolderwithCSVs'; 

    //Get an array with the folder names located at the root directory location 
    // The '0' is alphabetical ascending, '1' is descending. 
    $rootArray = scandir($rootPath, 0); 

    //Set Database Connection Details 
    $myServer = "SERVER"; 
    $myUser = "USER"; 
    $myPass = "PASSWORD"; 
    $myDB = "DATABASE"; 

    //Create connection to the database 
    $connection = odbc_connect("Driver={SQL Server};Server=$myServer;Database=$myDB;", $myUser, $myPass) or die("Couldn't connect to SQL Server on $myServer"); 

    //Extend Database Connection timeout 
    set_time_limit(10000); 

    //Set to true for bulk insert, set to false for line by line insert 
    // [If set to TRUE] - MUST HAVE BULK INSERT PERMISSIONS TO WORK 
    $bulkinsert = true; 

    //For loop that goes through the folders and finds CSV files 
    loopThroughAllCSVs($rootArray, $rootPath); 

    //Once procedure finishes, close the connection 
    odbc_close($connection); 

    function loopThroughAllCSVs($folderArray, $root){ 
     $fileFormat = '.csv'; 
     for($x = 2; $x < sizeof($folderArray); $x++){ 
      $eachFileinFolder = scandir($root."\\".$folderArray[$x]); 
      for($y = 0; $y < sizeof($eachFileinFolder); $y++){ 
       $fullCSV_path = $root."\\".$folderArray[$x]."\\".$eachFileinFolder[$y]; 
       if(substr_compare($fullCSV_path, $fileFormat, strlen($fullCSV_path)-strlen($fileFormat), strlen($fileFormat)) === 0){ 
        parseCSV($fullCSV_path); 
       } 
      } 
     } 
    } 

    function parseCSV($path){ 
     print_r($path); 
     print("<br>"); 
     if($GLOBALS['bulkinsert'] === false){ 
      $csv = array_map('str_getcsv', file($path)); 
      array_shift($csv);        //Remove Headers 

      foreach ($csv as $line){ 
       writeLinetoDB($line); 
      } 
     } 
     else{ 
      bulkInserttoDB($path); 
     } 
    } 

    function writeLinetoDB($line){ 
     $tablename = "[DATABASE].[dbo].[TABLE]"; 
     $insert = "INSERT INTO ".$tablename." (Column1,Column2,Column3,Column4,Column5,Column6,Column7) 
       VALUES ('".$line[0]."','".$line[1]."','".$line[2]."','".$line[3]."','".$line[4]."','".$line[5]."','".$line[6]."')"; 

     $result = odbc_prepare($GLOBALS['connection'], $insert); 
     odbc_execute($result)or die(odbc_error($connection)); 
    } 

    function bulkInserttoDB($csvPath){ 
     $tablename = "[DATABASE].[dbo].[TABLE]"; 
     $insert = "BULK 
        INSERT ".$tablename." 
        FROM '".$csvPath."' 
        WITH (FIELDTERMINATOR = ',', ROWTERMINATOR = '\\n')"; 

     print_r($insert); 
     print_r("<br>"); 

     $result = odbc_prepare($GLOBALS['connection'], $insert); 
     odbc_execute($result)or die(odbc_error($connection)); 
    } 
?> 

Я закончил с использованием сценария выше, чтобы написать в строке базы данных по линии ... Это собирается занять несколько часов. Я изменил сценарий на использование BULK INSERT, который, к сожалению, у нас не было «разрешений». Когда я получил «разрешения», метод BULK INSERT работал в обаянии.

Смежные вопросы