前段时间在工作中遇到了一个蛋疼的问题:某学校考场、监考老师、补考学生的自动安排的数据处理。由于业务要求,出现了大数据的存储。先来看看具体做法:
1、从考场数据池中获取到所有考场的基本信息。
2、随机抽取某个考场(同一场次不允许重复)
3、将补考考场信息写入考试信息数据库。
这没有问题,毕竟考场的数据不会太多。(这里我们叫步骤1)
下一步:
1、从监考老师数据池中获取到所有监考老师数据。
2、随机抽取某两位老师作为某考场的监考老师。
3、将监考老师数据写入考试信息数据库。
这也没有问题,因为监考老师数据也不会太大。(这里叫步骤2)
再下一步:
1、从补考学生报名情况获取所有补考学生的数据(此处有收费不收费之分但对数据没有很大影响)。
2、随机抽取考场所能容纳的最大考生人数到具体的某考场。
3、将学生数据写入考试信息数据库。(高潮在这里因为这里的数据会很大,一个学生会有N门考试科目)
这里问题出现了(这里叫步骤3)
(这里是winform程序所以相对来说还好处理一点)
那么在步骤1执行的时候我们可以在步骤1中随机获取到某个考场的时候就随机获取到监考老师的信息和参加考试的学生信息。
那么我们面临的是数据存储的时间长短问题。
第一种:可以选择边抽取考场边时就将抽取到的监考老师及学生写入数据库。
第二种:将考场先计算出来再抽取监考老师写入数据库然后再将学生单独处理,毕竟学生的数据量大些。
第三种:将所有的计算都在本地计算机完成(事先获取到相关信息存入本地内存)
毫无疑问,第一种是最慢的,舍弃之。
那么选择第二种或者第三种方法。这都需要对数据进行处理。
第一种大批量数据处理方法:
private void CopyListToTable(List<ExamDetail> randomStudens)
{
var dt = ConvertList.ConvertToDt<ExamDetail>(randomStudens);
SqlConnection conn = new SqlConnection(strConn);
DateTime beginTime = DateTime.Now;
conn.Open();
using (SqlBulkCopy sbc = new SqlBulkCopy(conn))
{
sbc.BatchSize = 10;
sbc.BulkCopyTimeout = 60;
sbc.NotifyAfter = 10000;
sbc.DestinationTableName = "K_Student_Exam_Address";
sbc.ColumnMappings.Add("TaskNumber", "task_no");
sbc.ColumnMappings.Add("StudentId", "student_id");
sbc.ColumnMappings.Add("StudentName", "student_name");
sbc.ColumnMappings.Add("ExamRoom", "examaddress");
sbc.ColumnMappings.Add("Semester", "semester");
sbc.WriteToServer(dt);
sbc.ColumnMappings.Clear();
}
DateTime endTime = DateTime.Now;
TimeSpan useTime = endTime - beginTime;
dt.Dispose();
var time = "使用时间" + useTime.TotalSeconds.ToString() + "秒";
conn.Close();
conn.Dispose();
}
此种方法为.net提供的SqlBulkCopy类中的海量数据存储可以同步大批量数据。(其中测试出2万行数据大约是3秒)。
第二种数据处理:
private void XmlUpdateStudents(List<ExamDetail> randomStudens)
{
var total = 10;
this.Visibled = "Visible";
var times = randomStudens.Count % total == 0 ? randomStudens.Count / total : randomStudens.Count / total + 1;
SqlConnection conn = new SqlConnection(strConn);
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@xml", SqlDbType.VarChar, -1);
cmd.CommandText = "Proc_AddBatchExam_Address";
DateTime beginTime = DateTime.Now;
var time = string.Empty;
for (int i = 0; i < times; i++)
{
var list = new List<ExamDetail>();
if (i == times - 1 && randomStudens.Count % total != 0)
{
list = randomStudens.GetRange(i * total, randomStudens.Count % total);
}
else
{
list = randomStudens.GetRange(i * total, total);
}
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("ExamDetails", "root");
dict.Add("ExamDetail", "obj");
dict.Add("TaskNumber", "task_no");
dict.Add("StudentId", "student_id");
dict.Add("StudentName", "student_name");
dict.Add("ExamRoom", "examaddress");
dict.Add("Semester", "semester");
var xmlStr = ConvertList.EntityToXml<ExamDetail>(list, dict);
cmd.Parameters["@xml"].SqlValue = xmlStr;
cmd.ExecuteNonQuery();
DateTime endTime = DateTime.Now;
TimeSpan useTime = endTime - beginTime;
time = "使用时间" + useTime.TotalSeconds.ToString() + "秒";
}
cmd.Dispose();
conn.Close();
conn.Dispose();
}
此种方法将数据分割成若干个xml字符串传入下列存储过程之中:
USE [run]
GO
/****** Object: StoredProcedure [dbo].[Proc_AddBatchExam_Address] Script Date: 10/15/2013 14:22:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[Proc_AddBatchExam_Address]
@xml nvarchar(max)
AS
Begin
BEGIN TRAN T
DECLARE @idoc INT
EXEC sp_xml_preparedocument @idoc out,@xml
INSERT INTO K_Student_Exam_Address
SELECT * FROM OPENXML(@idoc,'/root/obj',3)
WITH(
task_no VARCHAR(10),
student_id VARCHAR(14),
student_name VARCHAR(10),
examaddress VARCHAR(100),
semester VARCHAR(10)
)
--每次清除xml的预存数据
exec sp_xml_removedocument @idoc
IF @@ERROR>0
ROLLBACK
ELSE
COMMIT
END
此种方法为Sql对xml解析同步大批量数据。(其中测试出2万行数据大约是3秒以内)。
我选择了第二种,因为第二种看上去复杂一些,但是能自由的对数据进行操作与操作数据时能对外部程序进行操作而不受太大限制。
这是前段时间工作中遇到的问题记录下来,希望有相同问题的一起讨论。