我正在尝试找到访问巨大的Excel或CSV文件的方法,并执行聚合操作,如Sum,count和某些SQL操作,如Select,group by等。 我知道LINQ可以帮助我做到这一点。我更喜欢使用C#。我的问题是:
1)执行任何查询时,是否会将excel或CSV中的数据加载到内存中?所有文件大约12GB。我问的原因是我不希望应用程序挂起。
2)我可以创建一个Form应用程序,其中我有一个TextArea,其中列出了CSV / Excel的所有列。用户可以选择TextArea中的任何列。我打算使用SELECT,GROUP BY,SUM,AVERAGE等SQL选项。用户可以选择其中一个,并在内部使用LINQ构建查询并获得结果。结果可以存储在文本文件中。
我不确定这是否可行。请建议我这个。我是使用LINQ的新手。 如果通过LINQ无法做到这一点,你能否建议其他方法有效地做到这一点。
提前致谢。
答案 0 :(得分:1)
记忆不是你的问题 - 性能是。
我完全同意关于将此加载到正确的数据库中的注释,该数据库将对行等进行索引以使其更有效。话虽如此,通过直接阅读CSV,执行您描述的各种查询是可行和直接的(如果非常不可取)。
我认为在此任务中测试LINQtoCSV会很有趣。
没有现成的HUGE csv文件,我只使用以下标题生成了一个:
"row,Guid,Grouping1,Grouping2,Metric1,Metric2"
其中row
是行号,Guid
只是一个随机guid Grouping1
是0到49之间的随机int,Grouping2
是0到0之间的随机int 50,000和Metric1
& Metric2
是随机的,分别是双倍的。基本上只是一些随机数据。
然后我生成了一个带有99999999行的7Gb +文件...(我的目标是100,000,000,但在某个地方打了我的零索引盲点!)
所以好消息是LINQtoCSV很乐意解析这个坏孩子,而不必将文件加载到内存中。只需在csv中创建一个由行代表的类:
class Entry
{
[CsvColumn(FieldIndex = 1, Name="row")]
public long Id { get; set; }
[CsvColumn(FieldIndex = 2)]
public string Guid { get; set; }
[CsvColumn(FieldIndex = 3)]
public int Grouping1 { get; set; }
[CsvColumn(FieldIndex = 4)]
public int Grouping2 { get; set; }
[CsvColumn(FieldIndex = 5)]
public int Metric1 { get; set; }
[CsvColumn(FieldIndex = 6)]
public double Metric2 { get; set; }
}
然后将条目读入IEnumerable
:
IEnumerable<Entry> ReadEntries()
{
CsvFileDescription inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
CsvContext ctx = new CsvContext();
return ctx.Read<Entry>("test.csv", inputFileDescription);
}
你离开了。
你现在有一个功能齐全的IEnumerable,可以查询所有的GroupBy(),Select(),Sum(),Count()等以及Linq的所有其他类似SQL的乐趣。
只是一个小问题......
它不快!
使用这个简单的LINQtoCSV设置,我并行运行了3个简单的linq查询
使用此代码:
static IEnumerable<Entry> entries;
static void Main(string[] args)
{
entries = ReadEntries();
var tasks = new List<Task> {timeEntryCount,timeEntryCountWhereG1_equals_1,timeMetric2SumWhereG2_equals_1};
tasks.ForEach(t => t.Start());
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Entry count took " + timeEntryCount.Result);
Console.WriteLine("Entry count where G1==1 took " + timeEntryCountWhereG1_equals_1.Result);
Console.WriteLine("Metric1 sum where G2==1 took " + timeMetric2SumWhereG2_equals_1.Result);
Console.ReadLine();
}
static Task<TimeSpan> timeMetric2SumWhereG2_equals_1 = new Task<TimeSpan>(() =>
{
DateTime start = DateTime.Now;
double sum = entries
.Where(e => e.Grouping2 == 1)
.Sum(e=>e.Metric2);
Console.WriteLine("sum: " + sum);
DateTime end = DateTime.Now;
return end - start;
},TaskCreationOptions.LongRunning);
static Task<TimeSpan> timeEntryCountWhereG1_equals_1 = new Task<TimeSpan>(() =>
{
DateTime start = DateTime.Now;
long count = entries
.Where(e=>e.Grouping1==1)
.Count();
DateTime end = DateTime.Now;
Console.WriteLine("countG1: " + count);
return end - start;
}, TaskCreationOptions.LongRunning);
static Task<TimeSpan> timeEntryCount = new Task<TimeSpan>(() =>
{
DateTime start = DateTime.Now;
long count = entries.Count();
Console.WriteLine("CountAll: " + count);
DateTime end = DateTime.Now;
return end - start;
}, TaskCreationOptions.LongRunning);
现在可以肯定的是,这种狡猾的并行性并不是测试它的最佳方式,但我只是从臀部开始拍摄。
然而,我的合理快速SSD配备的笔记本电脑上的结果非常难看:
countG1: 2003023 CountAll: 99999999 sum: 1236810295.16543 Entry count took 00:25:26.3767852 Entry count where G1==1 took 00:24:41.9855814 Metric1 sum where G2==1 took 00:25:30.9080960
查询文件大约需要25分钟。如果这并不能让你把它变成一个正确索引的数据库,那么什么都不会!