我有一个程序,可以将大约 200 万行从数据库中读取到列表中。每一行都是一个包含地理坐标等信息的位置。
将数据添加到列表后,我使用 foreach 循环并获取坐标来创建 kml 文件。当行数很大时,循环会遇到内存不足异常错误(但在其他情况下可以完美工作)。
关于如何处理这个问题以便程序可以处理非常大的数据集的任何建议?KML 库是 SharpKML。
我仍然是C#的新手,所以请放轻松!
这是循环:
using (SqlConnection conn = new SqlConnection(connstring))
{
conn.Open();
SqlCommand cmd = new SqlCommand(select, conn);
using (cmd)
{
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
double lat = reader.GetDouble(1);
double lon = reader.GetDouble(2);
string country = reader.GetString(3);
string county = reader.GetString(4);
double TIV = reader.GetDouble(5);
double cnpshare = reader.GetDouble(6);
double locshare = reader.GetDouble(7);
//Add results to list
results.Add(new data(lat, lon, country, county, TIV, cnpshare, locshare));
}
reader.Close();
}
conn.Close();
}
int count = results.Count();
Console.WriteLine("number of rows in results = " + count.ToString());
//This code segment generates the kml point plot
Document doc = new Document();
try
{
foreach (data l in results)
{
Point point = new Point();
point.Coordinate = new Vector(l.lat, l.lon);
Placemark placemark = new Placemark();
placemark.Geometry = point;
placemark.Name = Convert.ToString(l.tiv);
doc.AddFeature(placemark);
}
}
catch(OutOfMemoryException e)
{
throw e;
}
这是列表中使用的类
public class data
{
public double lat { get; set; }
public double lon { get; set; }
public string country { get; set; }
public string county { get; set; }
public double tiv { get; set; }
public double cnpshare { get; set; }
public double locshare { get; set; }
public data(double lat, double lon, string country, string county, double tiv, double cnpshare,
double locshare)
{
this.lat = lat;
this.lon = lon;
this.country = country;
this.county = county;
this.tiv = tiv;
this.cnpshare = cnpshare;
this.locshare = locshare;
}
}
为什么在写入之前需要存储所有数据?与其将每一行添加到列表中,不如在读取时处理每一行,然后忘记它。
例如,尝试像这样将代码滚动在一起:
Document doc = new Document();
while (reader.Read())
{
// read from db
double lat = reader.GetDouble(1);
double lon = reader.GetDouble(2);
string country = reader.GetString(3);
string county = reader.GetString(4);
double TIV = reader.GetDouble(5);
double cnpshare = reader.GetDouble(6);
double locshare = reader.GetDouble(7);
var currentData = new data(lat, lon, country, county, TIV, cnpshare, locshare));
// write to file
Point point = new Point();
point.Coordinate = new Vector(currentData.lat, currentData.lon);
Placemark placemark = new Placemark();
placemark.Geometry = point;
placemark.Name = Convert.ToString(currentData.tiv);
doc.AddFeature(placemark);
}
不过,这只有在合理地实施Document
时才有效。
奥利弗是对的(我投赞成票)。性能方面,您可以做一些其他事情。首先,不要查询您不打算使用的字段。然后将所有变量声明(Oliver 的代码)移到 while 语句 (?) 之前。最后,与其等待 sql 服务器收集并发回所有记录,不如逐步执行步骤。例如,如果您的记录有一个 UID,并且获取它们的顺序是这个 UID,那么从本地 C# 变量"var lastID = 0"开始,将你的 select 语句更改为类似(预格式)"选择前 1000 ...其中 UID>lastID"并重复查询,直到您一无所获或任何东西将少于 1000 条记录。
如果用数据库中的数据填充列表没有太大的延迟,并且您没有提到用数据填充列表的问题,为什么不立即创建您的点和地标对象。代码如下。
var doc = new Document();
using (SqlConnection conn = new SqlConnection(connstring))
{
conn.Open();
SqlCommand cmd = new SqlCommand(select, conn);
using (cmd)
{
var reader = cmd.ExecuteReader();
while (reader.Read())
{
double lat = reader.GetDouble(1);
double lon = reader.GetDouble(2);
string country = reader.GetString(3);
string county = reader.GetString(4);
double TIV = reader.GetDouble(5);
double cnpshare = reader.GetDouble(6);
double locshare = reader.GetDouble(7);
var point = new Point();
point.Coordinate = new Vector(lat , lon );
var placemark = new Placemark();
placemark.Geometry = point;
placemark.Name = Convert.ToString(TIV);
doc.AddFeature(placemark);
reader.Close();
}
conn.Close();
}
如果没有充分的理由在内存中检索这么多数据,请尝试使用一些延迟加载方法。
> @drdigit,
我会避免循环执行查询。一个查询应始终返回当时所需的数据量。在本例中,您将有 1000 个返回 1000 行的查询。也许快速显示前 1000 行会更好,但我不确定如果您循环执行 1000 个更快的查询而不是只执行一个查询,它是否会更快。也许我错了...
我认为在这种情况下需要延迟加载,您的方法适用于延迟加载。