自制工具将SqlServer数据导入到SqlCE数据库

本文介绍了一款自定义工具,用于将SqlServer2008中的数据迁移到SqlCE数据库。该工具支持自定义数据类型映射、生成创建表及插入数据的脚本,并能够直接创建SqlCE数据库。

最近做一个SqlCE项目,需要频繁地从SqlServer2008服务器上提取数据到SqlCE数据库。

SqlServer2008中有生成数据脚本功能,可以通过脚本向导(右键点击数据库-〉任务-〉生成脚本)实现。但经常这么导数据太麻烦,一天导10几次能把你导崩溃。。。

突然想到动软代码生成器里边有生成数据脚本的功能。于是拿来参考着做了个SqlServer到SqlCE的数据转工具。

先晒一下界面:

主要功能:

1、输入数据库名和表名,生成创建SQLCE数据库的脚本和向SQKCE数据库中插入数据的脚本。

2、将脚本输出到文件。

3、直接生成SQLCE数据库。

实现方法

1、SqlServer中表结构的获取(根据动软代码生成器 )

要从创建表,首先要提取表的结构信息。以下代码组织了一个用于查询表结构的Sql语句。

 

 
  1. StringBuilder strSql = new StringBuilder(); 
  2. strSql.Append("SELECT "); 
  3. strSql.Append("colorder=C.column_id,"); 
  4. strSql.Append("ColumnName=C.name,"); 
  5. strSql.Append("TypeName=T.name, "); 
  6. //strSql.Append("Length=C.max_length, "); 
  7. strSql.Append("Length=CASE WHEN T.name='nchar' THEN C.max_length/2 WHEN
  8.  T.name='nvarchar' THEN C.max_length/2 ELSE C.max_length END,"); 
  9. strSql.Append("Preci=C.precision, "); 
  10. strSql.Append("Scale=C.scale, "); 
  11. strSql.Append("IsIdentity=CASE WHEN C.is_identity=1 THEN N'√'ELSE N'' END,"); 
  12. strSql.Append("isPK=ISNULL(IDX.PrimaryKey,N''),"); 
  13.  
  14. strSql.Append("Computed=CASE WHEN C.is_computed=1 THEN N'√'ELSE N'' END, "); 
  15. strSql.Append("IndexName=ISNULL(IDX.IndexName,N''), "); 
  16. strSql.Append("IndexSort=ISNULL(IDX.Sort,N''), "); 
  17. strSql.Append("Create_Date=O.Create_Date, "); 
  18. strSql.Append("Modify_Date=O.Modify_date, "); 
  19.  
  20. strSql.Append("cisNull=CASE WHEN C.is_nullable=1 THEN N'√'ELSE N'' END, "); 
  21. strSql.Append("defaultVal=ISNULL(D.definition,N''), "); 
  22. strSql.Append("deText=ISNULL(PFD.[value],N'') "); 
  23.  
  24. strSql.Append("FROM sys.columns C "); 
  25. strSql.Append("INNER JOIN sys.objects O "); 
  26. strSql.Append("ON C.[object_id]=O.[object_id] "); 
  27. strSql.Append("AND (O.type='U' or O.type='V') "); 
  28. strSql.Append("AND O.is_ms_shipped=0 "); 
  29. strSql.Append("INNER JOIN sys.types T "); 
  30. strSql.Append("ON C.user_type_id=T.user_type_id "); 
  31. strSql.Append("LEFT JOIN sys.default_constraints D "); 
  32. strSql.Append("ON C.[object_id]=D.parent_object_id "); 
  33. strSql.Append("AND C.column_id=D.parent_column_id "); 
  34. strSql.Append("AND C.default_object_id=D.[object_id] "); 
  35. strSql.Append("LEFT JOIN sys.extended_properties PFD "); 
  36. strSql.Append("ON PFD.class=1  "); 
  37. strSql.Append("AND C.[object_id]=PFD.major_id  "); 
  38. strSql.Append("AND C.column_id=PFD.minor_id "); 
  39. //strSql.Append("--AND PFD.name='Caption'
  40. //  -- 字段说明对应的描述名称(一个字段可以添加多个不同name的描述) "); 
  41. strSql.Append("LEFT JOIN sys.extended_properties PTB "); 
  42. strSql.Append("ON PTB.class=1 "); 
  43. strSql.Append("AND PTB.minor_id=0  "); 
  44. strSql.Append("AND C.[object_id]=PTB.major_id "); 
  45. //strSql.Append("-- AND PFD.name='Caption'  
  46. //-- 表说明对应的描述名称(一个表可以添加多个不同name的描述)   "); 
  47. strSql.Append("LEFT JOIN ");// -- 索引及主键信息 
  48. strSql.Append("( "); 
  49. strSql.Append("SELECT  "); 
  50. strSql.Append("IDXC.[object_id], "); 
  51. strSql.Append("IDXC.column_id, "); 
  52. strSql.Append("Sort=CASE INDEXKEY_PROPERTY(IDXC.[object_id],
  53. IDXC.index_id,IDXC.index_column_id,'IsDescending') "); 
  54. strSql.Append("WHEN 1 THEN 'DESC' WHEN 0 THEN 'ASC' ELSE '' END, "); 
  55. strSql.Append("PrimaryKey=CASE WHEN IDX.is_primary_key=1
  56.  THEN N'√'ELSE N'' END, "); 
  57. strSql.Append("IndexName=IDX.Name "); 
  58. strSql.Append("FROM sys.indexes IDX "); 
  59. strSql.Append("INNER JOIN sys.index_columns IDXC "); 
  60. strSql.Append("ON IDX.[object_id]=IDXC.[object_id] "); 
  61. strSql.Append("AND IDX.index_id=IDXC.index_id "); 
  62. strSql.Append("LEFT JOIN sys.key_constraints KC "); 
  63. strSql.Append("ON IDX.[object_id]=KC.[parent_object_id] "); 
  64. strSql.Append("AND IDX.index_id=KC.unique_index_id "); 
  65. strSql.Append("INNER JOIN  ");
  66. //对于一个列包含多个索引的情况,只显示第1个索引信息 
  67. strSql.Append("( "); 
  68. strSql.Append("SELECT [object_id], Column_id, index_id=MIN(index_id) "); 
  69. strSql.Append("FROM sys.index_columns "); 
  70. strSql.Append("GROUP BY [object_id], Column_id "); 
  71. strSql.Append(") IDXCUQ "); 
  72. strSql.Append("ON IDXC.[object_id]=IDXCUQ.[object_id] "); 
  73. strSql.Append("AND IDXC.Column_id=IDXCUQ.Column_id "); 
  74. strSql.Append("AND IDXC.index_id=IDXCUQ.index_id "); 
  75. strSql.Append(") IDX "); 
  76. strSql.Append("ON C.[object_id]=IDX.[object_id] "); 
  77. strSql.Append("AND C.column_id=IDX.column_id  "); 
  78. strSql.Append("WHERE O.name=N'" + TableName + "' "); 
  79. strSql.Append("ORDER BY O.name,C.column_id  "); 

TableName为用户输入的表名称。查询结果是一个包含了表详细信息的DataTable(通过ADO.NET的SqlDataAdapter将数据Fill到DataSet中)。

有了这些信息我们就可以生成创建表的语句了。(注:这些语句不适合SqlServer2000。具体这些语句是什么意思,问老李吧。。我也看不是很明白。)

2、自定义一个SqlServer中数据类型到SQLCE数据类型的映射。

SqlCE只支持18种数据类型,比SqlServer少的多。因此如果原数据库中有SqlCE不支持的数据类型,需要手动将数据类型映射为SqlCE数据类型。具体怎样映射,应可以根据需要手动设置。

还有一些用户自定义的数据类型,在SqlCE中是不可以使用的。也需要映射。下面这个类完成数据的映射功能。

首先编辑一个数据映射表(TypeMap.txt),保存一些如下面格式的数据:

bigint=bigint;

binary=binary;

char=nchar;

date=datetime;

decimal=float;

nvarchar=nvarchar;

varchar=nvarchar;

real=real;

smalldatetime=datetime;

smallint=smallint;

smallmoney=money;

text=ntext;

tinyint=tinyint;

uniqueidentifier=uniqueidentifier;

varbinary=varbinary;

NGUD=uniqueidentifier

前面是SqlServer中的数据类型,后边是想要映射到SqlCE中的数据类型,中间一分号隔开。这个映射可以根据需要手动调整。

下边类中的GetMapedDataType()函数返回映射后的数据类型。

 
  1. public class TypeMap 
  2. //获取数据库(SqlServer和SqlCE)字段类型映射 
  3. private static string datafile =
  4.  System.Windows.Forms.Application.StartupPath + "//TypeMap.txt"
  5.     
  6. //加载数据类型映射表 
  7. private static  string[] LoadTypeMap() 
  8. string s = string.Empty; 
  9. if (File.Exists(datafile)) 
  10. StreamReader sr = new StreamReader(datafile, Encoding.UTF8); 
  11. //读取数据到字符串中 
  12. s = sr.ReadToEnd(); 
  13. sr.Close(); 
  14. //替换掉字符串中的回车换行符 
  15. s= s.Replace("/r/n"""); 
  16. return s.Split(';'); 
  17.  
  18. /// <summary> 
  19. /// 获取数据库(SqlServer和SqlCE)字段类型映射 
  20. /// </summary> 
  21. /// <param name="sourceType">SqlServer数据类型</param> 
  22. /// <returns>SqlCE数据类型</returns> 
  23. public static string GetMapedDataType(string sourceType) 
  24. string[] ss = TypeMap.LoadTypeMap(); 
  25. //在映射表中查找原始类型映射后的数据类型 
  26. foreach (string s in ss) 
  27. string[] sss = s.Split('='); 
  28. if (sss[0].ToLower() == sourceType.ToLower()) 
  29. //如果找到,则返回映射后的类型 
  30. return sss[1]; 
  31. //找不到,返回输入的类型 
  32. return sourceType; 

3、生成创建表的语句(create table….)

 
  1. StringBuilder strclass = new StringBuilder(); 
  2.  //strclass.AppendLine("if exists (select * from
  3.  //sysobjects where id = OBJECT_ID('[" + tablename + "]')
  4.  //and OBJECTPROPERTY(id, 'IsUserTable') = 1) "); 
  5.  //strclass.AppendLine("DROP TABLE [" + tablename + "]"); 
  6.  
  7.  //string PKfild = "";//主键字段 
  8.  //bool IsIden = false;//是否是标识字段 
  9.  StringBuilder ColdefaVal = new StringBuilder();//字段的默认值列表           
  10.  
  11.  Hashtable FildtabList = new Hashtable();//字段列表(字段名,字段类型) 
  12.  StringBuilder FildList = new StringBuilder();//字段列表 
  13.  
  14. DataTable dt = (1中语句查询后得到的表); //包含表详细信息的DataTable 
  15.  
  16.  //开始生成创建表语句。 
  17.  //建表第一句…..终于开始了 
  18.  strclass.AppendLine("CREATE TABLE [" + tablename + "] ("); 
  19.  
  20.  if (dt != null) 
  21.  { 
  22.      DataRow[] dtrows; 
  23.      dtrows = dt.Select();  //这里可以做点简单的筛选 
  24.  
  25.      ////也可以这样 
  26.      //foreach (DataRow row in dt.Rows) 
  27.      //{ 
  28.      //   //。。。。。。。  
  29.      //} 
  30.  
  31.      foreach (DataRow row in dtrows) 
  32.      { 
  33. string columnName = row["ColumnName"].ToString(); 
  34. string columnType = TypeMap.GetMapedDataType(row["TypeName"].ToString());
  35. //转换后的数据类型 
  36.  
  37. string IsIdentity = row["IsIdentity"].ToString(); 
  38. string Length = row["Length"].ToString(); 
  39. string Preci = row["Preci"].ToString(); 
  40. string Scale = row["Scale"].ToString(); 
  41. string ispk = row["isPK"].ToString(); 
  42. string isnull = row["cisNull"].ToString(); 
  43. string defaultVal = row["defaultVal"].ToString(); 
  44.  
  45. strclass.Append("[" + columnName + "] [" + columnType + "] "); 
  46.  
  47. switch (columnType.Trim()) 
  48.   case "varchar"
  49.   case "char"
  50.   case "nchar"
  51.   case "binary"
  52.   case "nvarchar"
  53.   case "varbinary"
  54.       { 
  55.  string len = this.GetDataTypeLenVal(columnType.Trim(), Length);
  56. //获取字符串长度(注2) 
  57.  strclass.Append(" (" + len + ")"); 
  58.       } 
  59.       break
  60.   case "decimal"
  61.   case "numeric"
  62.       strclass.Append(" (" + Preci + "," + Scale + ")"); 
  63.       break
  64. if (isnull == "√"
  65.   strclass.Append(" NULL"); 
  66. else 
  67.   strclass.Append(" NOT NULL"); 
  68. if (defaultVal != ""
  69.   strclass.Append(" DEFAULT " + defaultVal); 
  70. strclass.AppendLine(","); 
  71.  
  72. FildtabList.Add(columnName, columnType); 
  73. FildList.Append("[" + columnName + "],"); 
  74.  
  75. //if(defaultVal!="") 
  76. //{ 
  77. //  ColdefaVal.Append("CONSTRAINT [DF_"+tablename+"_"+columnName+"]
  78. // DEFAULT "+defaultVal+" FOR ["+columnName+"],"); 
  79. //} 
  80.  
  81. //if((ispk=="√")&&(PKfild=="")) 
  82. //{                      
  83. //    PKfild=columnName;//得到主键 
  84. //} 
  85.      } 
  86.  } 
  87.  string s = strclass.ToString(); 
  88.  
  89.             //去掉strclass()中最后一个豆号 
  90.  int n = s.LastIndexOf(','); 
  91.  string ss = s.Substring(0, n) + ")"
  92.  strclass.Remove(0, strclass.ToString().Length); 
  93.  strclass.Append(ss); 
  94.  strclass.Append(";");//添加一个分号(;)用于分割字符串 
  95.  strclass.AppendLine(""); 
  96.  strclass.AppendLine(""); 
  97.  
  98.             //去掉FildList(字段列表)中最后一个豆号 
  99.  s = FildList.ToString(); 
  100.  n = s.LastIndexOf(','); 
  101.  ss = s.Substring(0, n); 
  102.  FildList.Remove(0, FildList.ToString().Length); 
  103.  FildList.Append(ss); 
  104.  
  105.  //开始获取表中数据,dtdata中包含了查询数据库得到的数据 
  106.  DataTable dtdata = this.getData(dbname,tablename); 
  107.  //生成插入数据语句 
  108.  if (dtdata != null) 
  109.  { 
  110.      foreach (DataRow row in dtdata.Rows)//循环表数据 
  111.      { 
  112. StringBuilder strfild = new StringBuilder(); 
  113. StringBuilder strdata = new StringBuilder(); 
  114. //字段数组 
  115. string[] split = FildList.ToString().Split(new Char[] { ',' }); 
  116.  
  117. foreach (string fild in split)//循环一行数据的各个字段 
  118.   string colname = fild.Substring(1, fild.Length - 2); 
  119.   string coltype = ""
  120.   foreach (DictionaryEntry myDE in FildtabList) 
  121.   { 
  122.       if (myDE.Key.ToString() == colname) 
  123.       { 
  124.  coltype = myDE.Value.ToString(); 
  125.       } 
  126.   } 
  127.   string strval = ""
  128.   switch (coltype) 
  129.   { 
  130.       case "binary"
  131.  { 
  132.    //我的数据库中没有binary型数据,这里没有处理; 
  133.  } 
  134.  break
  135.       case "bit"
  136.  { 
  137.    strval = (row[colname].ToString().ToLower() == "true") ? "1" : "0"
  138.  } 
  139.  break
  140.       default
  141.  strval = row[colname].ToString().Trim().Replace(';',';'); 
  142.  break
  143.   } 
  144.   strdata.Append("'" + strval.Replace('/'''°') + "',"); //字段中剔除分号“’” 
  145.   strfild.Append("[" + colname.Replace('/'''°') + "],");//数据中剔除分号“’” 
  146.                      
  147.                     //还是三删除最后一个逗号 
  148. n = strdata.ToString().LastIndexOf(','); 
  149. ss = strdata.ToString().Substring(0, n); 
  150. strdata.Remove(0,strdata.ToString().Length); 
  151. strdata.Append(ss); 
  152.  
  153. n = strfild.ToString().LastIndexOf(','); 
  154. ss = strfild.ToString().Substring(0, n); 
  155. strfild.Remove(0, strfild.ToString().Length); 
  156. strfild.Append(ss); 
  157.  
  158.  
  159. //导出数据INSERT语句 
  160. strclass.Append("INSERT [" + tablename + "] ("); 
  161. strclass.Append(strfild.ToString());  //插入字段集 
  162. strclass.Append(") VALUES ( "); 
  163. strclass.Append(strdata.ToString().Replace(';',';');
  164. //数据值。并将英文分号替换为全角分号。 
  165. strclass.AppendLine(");"); 
  166.                 //一条插入语句完成 
  167.      } 
  168.  } 
  169.          
  170.  //删除语句中最后一个分号 
  171.  n = strclass.ToString().LastIndexOf(';'); 
  172.  ss = strclass.ToString().Substring(0,n); 
  173.  strclass.Remove(0, strclass.ToString().Length); 
  174.  strclass.Append(ss); 
  175.  
  176.  return strclass.ToString(); 
  177. }  

注:1、这里使用分号将每条语句隔开,是为了使用的方便。因为SqlCE不能一次执行多条语句,向表中插入数据只能一条条地循环。用分号分割后方便以后的使用。

2、GetDataTypeLenVal函数来自动软代码生成器。没必要列出

4、使用脚本生成SqlCE数据库。

代码如下,说明很详细就不啰嗦了

首先添加引用System.Data.SqlServerCe的引用:

之后再代码中添加对SqlServerCE的引用:

using System.Data.SqlServerCe;

开始:

 
  1. public class SDF 
  2. string fileName = "C://123.sdf"//要创建的SQLCE数据库位置 
  3. public SDF(string file) 
  4. fileName = file; 
  5. /// <summary> 
  6. /// 创建数据库函数 
  7. /// </summary> 
  8. /// <param name="file"></param> 
  9. private void CreateDB(string file) 
  10. fileName = file; 
  11. //SQLCE连接字符串 
  12. string connStr = "Data Source=" + file; 
  13. try 
  14. if (File.Exists(file)) 
  15. File.Delete(file); 
  16. //在指定位置创建数据库 
  17. SqlCeEngine eng = new SqlCeEngine(); 
  18. eng.LocalConnectionString = connStr; 
  19. eng.CreateDatabase(); 
  20. catch (Exception exp) 
  21. throw exp; 
  22.  
  23. //执行传入的脚本,在SqlCE数据库中创建表并插入数据 
  24. private void CreateTab(string ScriptStr) 
  25. string connStr = "Data Source=" + fileName; 
  26. string[] ss = ScriptStr.Split(';');//分割脚本语句。 
  27. using (SqlCeConnection conn = new SqlCeConnection(connStr)) 
  28. //SQLCE中逐条执行语句。。。 
  29. SqlCeCommand cmd = new SqlCeCommand(); 
  30. cmd.Connection = conn; 
  31. conn.Open(); 
  32. foreach (string sqlstr in ss) 
  33. cmd.CommandText = sqlstr;//循环 
  34. cmd.ExecuteNonQuery(); 
  35. conn.Close(); 
  36.  
  37. //通过文件名和脚本文件组装数据库 
  38. public void Create(string File, string Script) 
  39. this.CreateDB(File); 
  40. this.CreateTab(Script); 

在程序中调用方法

 
  1. if (this.saveToFile.ShowDialog() == DialogResult.OK) 
  2.     SDF sdf = new SDF("");//懒得输入了。。其实不输入文件名也行 
  3.     string filaname = saveToFile.FileName;//只要在这里将文件名传进去就行了 
  4.     //将文本写入SqlCE数据库 
  5.     sdf.Create(filaname, this.GetCode()); 

5、字段类型映射的设置方法:

在下边界面中输入映射类型,前边是原始字段类型后边是SqlCE支持的字段类型

6、总结

分析表的结构,然后组织创建表语句-〉查询数据,组织插入语句。读取文件中字符串映射,替换原来映射。

为了省事,没有设置主键;对所有字段都进行操作,不管是不是空。

虽然效率不高,比手工快多了。。呵呵

测试中,对九千多条数据2-3秒就可以完事。数据量太大就不行了。

60万条数据能让我的几近崩溃。。。原因很简单:string字符串太长,而且全部保存在内存中。可是谁家PDA中方60万条数据呢?暂时不动了。。。

不会插附件,本来想把程序包也放上去。。。。

最后感谢李天平老师的无私奉献。。。 

一个将excel文件导入到SQLServer表中的程序 一 双击Input.exe运行程序,将弹出一个窗口,这时请你在"数据库名"后面的 输入栏中输入数据库名(如果是千方百剂就是输入帐套名).你如果没对数 据库的登录进行特殊修改的话,那"用户名和密码"就没必要修改了. 二 填好以上输入框后,真接单击"连接数据库",如果连接成功,将弹出"数据库 连接成功,你现在可以导入数据"的对话框,你按"OK"后将弹出新的一个数据 导入的窗口. 三 在这个窗口上单击"打开EXCEL文件"按钮,然后选择你要导入的Excel文件, 按打开(这时如果你数据比较多的话你可能要多等一会儿时间),之后就弹 出一个让你选择Excel工作区的窗口,你可以在下拉框中选择你数据所在的 Excel工作区了.选完以后按确定,你可以看到你Excel里的数据已经在"Excel 数据信息"里面了.而且还可以看到多了一列"不导入"的选项了.你如果哪一行 的数据不导入的话你可以打勾,这一行将不被导入. 四 做完以上三步后,请在"表名"后面的下拉框中选择你所要导入的表的名称. 选完后,你得到"数据转换信息如下"这一栏配置数据转换的对应关系. 五 双击Excel字段处从下拉框中选择excel的列,双击表字段处从下拉框中选 择SQL表的列,然后看这列是否是"关键字",是的话打勾,不是不打勾.选择 完第一行后,就按方向键的向下键,继续第二行的选择,直到配置完Excel列 和表字段的对应关系为止. 六 按"导入数据"按钮系统会自动将页面转到"转换信息"这一页面.你将可以看 到第几行导入成功,或第几行导入失改的信息.
sql server ce 的工具 非常有用 SQL Server 2005 移动版(SQL Server Mobile)或SQL Server 2000 Windows CE 2.0版(SQL Server CE 2.0)的企业和个人用户如果计划与SQL Server 2000或SQL Server 2005数据库保持同步,需要在您运行Microsoft Internet Information Services(IIS)的服务器上安装互联工具。 本页内容表述的是互联工具。请注意文件名中的“xx”是代表安装语言的标识符。最初仅有英文互联工具信息(“en”即标识符“XX”)。为满足向后兼容的需要,我们提供SQL Server CE 2.0复制软件。 请查阅安装互联工具的设置说明。 1. Microsoft SQL Server 2005移动版服务器工具(sqlce30setupxx.msi)在IIS 箱中安装SQL Server Mobile复制组件。 这个组件用于把移动设备中的SQL Server Mobile连接到SQL Server 2005、SQL Server 2000 SP3a、及 SQL Server 2000 SP4数据库。 2. Microsoft SQL Server 2000 Service Pack 4复制组件(sql2kxxsp4.msi)在IIS机器中安装SQL Server 2000 SP3a复制组件。这个组件用于把移动设备中的SQL Mobile数据库连接到SQL Server 2000 SP4数据库。 3. Microsoft SQL Server 2000 Service Pack 3a复制组件(sql2kxxsp3a.msi)在IIS机器中安装 SQL Server CE 2.0 及 SQL Server 2000 SP4 复制组件。 该组件用于把移动设备中的SQL Server CE 2.0数据库连接到SQL Server 2000 SP3a数据库。 4. 用于SQL Server 2000 SP4 (sqlce20sql2ksp4.exe)的SQL Server CE 2.0 复制组件在IIS机器中安装 SQL Server CE 2.0 及 SQL Server 2000 SP4 复制组件。该组件用于把移动设备中的SQL Server CE 2.0数据库连接到SQL Server 2000 SP4。 5. 用于SQL Server 2000 SP3a (sqlce20sql2ksp3a.exe)的SQL Server CE 2.0复制组件在IIS机器中安装SQL Server CE 2.0 及 SQL Server 2000 SP3a 复制组件。该组件用于把移动设备中的SQL Server CE 2.0数据库连接到SQL Server 2000 SP3a 数据库
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值