如何获取CSV字段并写入SQL中的列

时间:2014-06-07 15:38:35

标签: c# sql sql-server csv

我有以下代码,它采用CSV并写入控制台:

using (CsvReader csv = new CsvReader(
           new StreamReader("data.csv"), true))
    {
        // missing fields will not throw an exception,
        // but will instead be treated as if there was a null value
        csv.MissingFieldAction = MissingFieldAction.ReplaceByNull;
        // to replace by "" instead, then use the following action:
        //csv.MissingFieldAction = MissingFieldAction.ReplaceByEmpty;
        int fieldCount = csv.FieldCount;
        string[] headers = csv.GetFieldHeaders();
        while (csv.ReadNextRecord())
        {
            for (int i = 0; i < fieldCount; i++)
                Console.Write(string.Format("{0} = {1};",
                              headers[i],
                              csv[i] == null ? "MISSING" : csv[i]));
            Console.WriteLine();
        }
    }

CSV文件有7个标题,我的SQL表中有7列。

将每个csv[i]和每个列的行写入一行然后移到下一行的最佳方法是什么?

我尝试将ccsv[i]添加到字符串数组中,但这不起作用。

我也尝试了以下内容:

SqlCommand sql = new SqlCommand("INSERT INTO table1 [" + csv[i] + "]", mysqlconnectionstring);
sql.ExecuteNonQuery();

我的表格(table1)是这样的:

name address city zipcode phone fax device

3 个答案:

答案 0 :(得分:1)

你的问题很简单,但我会更进一步,让你知道一个更好的方法来处理这个问题。

当您遇到问题时,请务必将其分解为零件并将每个零件应用于每个方法中。例如,在您的情况下:

  • 1 - 从文件中读取
  • 2 - 创建一个SQL查询
  • 3 - 运行查询

你甚至可以添加验证到文件中(想象你的文件在一行或多行中甚至没有7个字段......)以及下面的例子,只有你的文件永远不会传递大约500行,就好像它通常你应该考虑使用一个SQL语句将你的文件直接存入数据库,它被称为bulk insert

1 - 从文件中读取:

我会使用List<string>来保存行条目,并且我总是使用StreamReader来读取文本文件。

            using (StreamReader sr = File.OpenText(this.CsvPath))
            {
                while ((line = sr.ReadLine()) != null)
                {
                    splittedLine = line.Split(new string[] { this.Separator }, StringSplitOptions.None);

                    if (iLine == 0 && this.HasHeader)
                        // header line
                        this.Header = splittedLine;
                    else
                        this.Lines.Add(splittedLine);

                    iLine++;
                }
            }

2 - 生成sql

        foreach (var line in this.Lines)
        {
            string entries = string.Concat("'", string.Join("','", line))
                                   .TrimEnd('\'').TrimEnd(','); // remove last ",'" 

            this.Query.Add(string.Format(this.LineTemplate, entries));
        }

3 - 运行查询

SqlCommand sql = new SqlCommand(string.Join("", query), mysqlconnectionstring);
sql.ExecuteNonQuery();

有一些乐趣我最终做了解决方案,你可以在这里下载,输出是:

enter image description here

<强> The code can be found here 即可。它需要更多的调整,但我会留给其他人。解决方案用C#编写,VS 2013。

ExtractCsvIntoSql类如下:

public class ExtractCsvIntoSql
{
    private string CsvPath, Separator;
    private bool HasHeader;
    private List<string[]> Lines;
    private List<string> Query;

    /// <summary>
    /// Header content of the CSV File
    /// </summary>
    public string[] Header { get; private set; }

    /// <summary>
    /// Template to be used in each INSERT Query statement
    /// </summary>
    public string LineTemplate { get; set; }

    public ExtractCsvIntoSql(string csvPath, string separator, bool hasHeader = false)
    {
        this.CsvPath = csvPath;
        this.Separator = separator;
        this.HasHeader = hasHeader;
        this.Lines = new List<string[]>();

        // you can also set this
        this.LineTemplate = "INSERT INTO [table1] SELECT ({0});";
    }

    /// <summary>
    /// Generates the SQL Query
    /// </summary>
    /// <returns></returns>
    public List<string> Generate()
    {
        if(this.CsvPath == null)
            throw new ArgumentException("CSV Path can't be empty");

        // extract csv into object
        Extract();
        // generate sql query 
        GenerateQuery();

        return this.Query;
    }

    private void Extract()
    {
        string line;
        string[] splittedLine;
        int iLine = 0;

        try
        {
            using (StreamReader sr = File.OpenText(this.CsvPath))
            {
                while ((line = sr.ReadLine()) != null)
                {
                    splittedLine = line.Split(new string[] { this.Separator }, StringSplitOptions.None);

                    if (iLine == 0 && this.HasHeader)
                        // header line
                        this.Header = splittedLine;
                    else
                        this.Lines.Add(splittedLine);

                    iLine++;
                }
            }
        }
        catch (Exception ex)
        {
            if(ex.InnerException != null)
                while (ex.InnerException != null)
                    ex = ex.InnerException;

            throw ex;
        }

        // Lines will have all rows and each row, the column entry
    }

    private void GenerateQuery()
    {
        foreach (var line in this.Lines)
        {
            string entries = string.Concat("'", string.Join("','", line))
                                   .TrimEnd('\'').TrimEnd(','); // remove last ",'" 

            this.Query.Add(string.Format(this.LineTemplate, entries));
        }
    }
}

您可以将其运行为:

class Program
{
    static void Main(string[] args)
    {
        string file = Ask("What is the CSV file path? (full path)");
        string separator = Ask("What is the current separator? (; or ,)");

        var extract = new ExtractCsvIntoSql(file, separator);
        var sql = extract.Generate();

        Output(sql);
    }

    private static void Output(IEnumerable<string> sql)
    {
        foreach(var query in sql)
            Console.WriteLine(query);

        Console.WriteLine("*******************************************");
        Console.Write("END ");
        Console.ReadLine();
    }

    private static string Ask(string question)
    {
        Console.WriteLine("*******************************************");
        Console.WriteLine(question);
        Console.Write("= ");
        return Console.ReadLine();
    }
}

答案 1 :(得分:1)

通常我喜欢更通用,所以我会尝试解释我不时使用的非常基本的流程:

我不喜欢硬编码的态度,所以即使你的代码能够运行,它也会专门用于一种类型。我更喜欢简单的反思,首先要了解DTO是什么,然后了解我应该使用哪个存储库来操作它:

例如:

public class ImportProvider
{
    private readonly string _path;
    private readonly ObjectResolver _objectResolver;

    public ImportProvider(string path)
    {
        _path = path;
        _objectResolver = new ObjectResolver();
    }

    public void Import()
    {
        var filePaths = Directory.GetFiles(_path, "*.csv");
        foreach (var filePath in filePaths)
        {
            var fileName = Path.GetFileName(filePath);
            var className = fileName.Remove(fileName.Length-4);
            using (var reader = new CsvFileReader(filePath))
            {
                var row = new CsvRow();
                var repository = (DaoBase)_objectResolver.Resolve("DAL.Repository", className + "Dao");
                while (reader.ReadRow(row))
                {
                    var dtoInstance = (DtoBase)_objectResolver.Resolve("DAL.DTO", className + "Dto");
                    dtoInstance.FillInstance(row.ToArray());
                    repository.Save(dtoInstance);
                }
            }
        }
    }
}

上面是一个负责导入数据的非常基础的类。不过这段代码如何解析CSV文件(CsvFileReader),重要的部分是那个&#34; CsvRow&#34;是一个简单的列表。

以下是ObjectResolver的实现:

public class ObjectResolver
{
    private readonly Assembly _myDal;
    public ObjectResolver()
    {
       _myDal = Assembly.Load("DAL");

    }
    public object Resolve(string nameSpace,  string name)
    {
        var myLoadClass = _myDal.GetType(nameSpace + "." + name); 
        return Activator.CreateInstance(myLoadClass);
    }

}

我的想法是简单地遵循命名对话,在我的情况下是使用&#34; Dto&#34;用于反映实例的后缀,以及&#34; Dao&#34;用于反映负责任的dao的后缀。 Dto或Dao的全名可以从csv名称或标题中获取(如您所愿)

下一步是填写Dto,每个dto或实现以下简单摘要:

public abstract class DtoBase
{
    public abstract void FillInstance(params string[] parameters);
}

因为每个Dto&#34;都知道&#34;他的结构(就像你知道在数据库中创建一个合适的表),它可以轻松实现FillInstanceMethod,这里有一个简单的Dto示例:

public class ProductDto : DtoBase
{
    public int ProductId { get; set; }
    public double Weight { get; set; }
    public int FamilyId { get; set; }

    public override void FillInstance(params string[] parameters)
    {
        ProductId = int.Parse(parameters[0]);
        Weight = double.Parse(parameters[1]);
        FamilyId = int.Parse(parameters[2]);
    }
}

在您的Dto填充数据后,您应找到适当的Dao来处理它 这基本上发生在Import()方法的这一行的反射中:

var repository = (DaoBase)_objectResolver.Resolve("DAL.Repository", className + "Dao");

在我的例子中,Dao实现了一个抽象基类 - 但它与你的问题没有关系,你的DaoBase可以是一个带有单个Save()方法的简单抽象。 通过这种方式,你有一个专门的Dao to CRUD你的Dto - 每个Dao只知道如何为其相关的Dto保存。以下是ProductDto的相应ProductDao:

public class ProductDao : DaoBase
{
    private const string InsertProductQuery = @"SET foreign_key_checks = 0;
                                                Insert into product (productID, weight, familyID)
                                                VALUES (@productId, @weight, @familyId);
                                                SET foreign_key_checks = 1;";


    public override void Save(DtoBase dto)
    {
        var productToSave = dto as ProductDto;
        var saveproductCommand = GetDbCommand(InsertProductQuery);
        if (productToSave != null)
        {
            saveproductCommand.Parameters.Add(CreateParameter("@productId", productToSave.ProductId));
            saveproductCommand.Parameters.Add(CreateParameter("@weight", productToSave.Weight));
            saveproductCommand.Parameters.Add(CreateParameter("@familyId", productToSave.FamilyId));
            ExecuteNonQuery(ref saveproductCommand);
        }

    }
}

请忽略CreateParameter()方法,因为它是基类的抽象。你可以使用CreateSqlParameter或CreateDataParameter等。

请注意,这是一个真正天真的实施 - 你可以根据自己的需要轻松改造它。

答案 2 :(得分:0)

从你问题的第一印象我想你会有大量的记录(超过lacs)。如果是,我会认为SQL批量复制一个选项。如果记录会减少单个记录。插入。插入不起作用的原因是你没有提供表的所有列,也有一些语法错误。