排序、搜索

排序、搜索

上周六,写了第一篇博客《订餐系统之权限设计》,在此感谢那些鼓励、关注我的园友们,更要感谢那些提出宝贵建议的朋友们。看了你们的评论,才真切的感受到:朋友们的评论往往会让文章更有看点。上篇文章中 郑明人生就是赌  等几个园友的留言让我对我们系统的权限优化有了方向。当然,这样的优化肯定不是一天两天的事,做技术的朋友应该都知道:一个难题经常啃啃,某天也许就有了好的方案了(近段时间啃掉了几个2、3年前未处理好的的问题,才想起初中数学老师让我们经常啃一些竞赛题的良苦用心),今天的文章说的就是一个从2010年就想优化,但一直未优化好的功能,也从 幸福框架的评论中,看到了他的博客,更是从他博客的留言中,找到了处理:按日期+6位顺序号生成订单编号(主要处理并发的情况)的方案,之前有客户要这样生成订单编号,我只能回复实现不了,因为当时只知道,每次获取最大的订单编号,处理不了并发的情况,惭愧。

  关于这个标题,我还是交代下背景吧。这个问题从2010年第一次实现时,就觉得当时那种方案太差了,自己都看不去,只因没有别的办法,从那以后,每每得空,就拿出来琢磨下,现在这个方案也许还有不少问题,也希望各位指点下。我们是做订餐系统的,主要实体就是商家(有坐标)和用户(有坐标)。所以就有这么个需求,返回距离N公里内的商家,按距离从近到远排序。先看下,数据库设计吧,如图(1):

                         

                                                                                              图(1)

   下面我先介绍下这几个表的关系吧:

    ETogo :商家表,dataid表示商家编号,togoname表示商家名称。

    ETogoLocalInfo:商家定位表,togoid对应etogo.dataid,lat表示商家纬度,lng表示商家经度(经纬度在地图上标注所得)

        EAddress :用户地址表,保存用户的经纬度,lat表示用户纬度,lng表示用户经度(经纬度在地图上标注所得)

   生活的经验告诉我们:一条成功的路,背后总有数不完的错误的路。下面我把那些曾经错误的路也写下来,以作对比。

2010年方案

   当时年少,对sql基本只会简单的select,更多的东西依赖于应用程序,于是有了下面的代码,由于对第二页的处理不了,所以用了方法:getDistancetogoid获取前N页的id然后再处理,里面关于距离的语句每次同事用到都抱怨,如果再加的N公里内的,这个语句就麻烦了。

  dal层代码
/// <summary>
        /// 获取列表,返回距离排序(出现商家重复的现象目前不采用)
        /// </summary>
        /// <param name="pagesize">每页大小</param>
        /// <param name="pageindex">当前页数</param>
        /// <param name="strWhere">搜索条件</param>
        /// <param name="orderName">排序字段</param>
        /// <param name="orderType">排序类型</param>
        /// <param name="mylat">用户纬度</param>
        /// <param name="mylng">用户经度</param>
        /// <returns></returns>
        public IList<TogoInfo> GetDistanceList(int pagesize, int pageindex, string strWhere, string orderName, int orderType, string mylat, string mylng)
        {
            IList<TogoInfo> infos = new List<TogoInfo>();
            string ids = "";
            if (pageindex > 1)
            {
                ids = getDistancetogoid(pagesize, pageindex, strWhere, orderName, orderType, mylat, mylng);
                if (ids == "")
                {
                    return infos;
                }
            }
            SqlParameter[] parameters = 
            {
                new SqlParameter("@tblName", SqlDbType.VarChar,255),
                new SqlParameter("@strGetFields", SqlDbType.VarChar,2000),
                new SqlParameter("@primary", SqlDbType.VarChar,255),
                new SqlParameter("@orderName", SqlDbType.VarChar,255),
                new SqlParameter("@PageSize", SqlDbType.Int),
                new SqlParameter("@PageIndex", SqlDbType.Int),
                new SqlParameter("@OrderType", SqlDbType.Bit),
                new SqlParameter("@strWhere", SqlDbType.VarChar,4500),
                new SqlParameter("@ids", SqlDbType.VarChar,2000)
            };
            string orderstr = orderType > 0 ? "desc" : "asc";
            parameters[0].Value = "etogo";
            string field = "*, (select Lat from ETogoLocalInfo where togoid = etogo.dataid) as lat ,(select lng from ETogoLocalInfo where togoid = etogo.dataid) as lng, (select lastlogindate from ETogoPrinter where ETogoPrinter.togoid =  etogo.dataid ) lastlogindate,(select (Case when DateDiff(mi,LastLoginDate,getdate()) < 5 then 1 else 0 end) from etogoprinter where ETogoPrinter.togoid =  etogo.dataid)  as online";
            field += ",(( 6371 * acos( cos( radians(" + mylat + ") ) * cos( radians( (select Lat from ETogoLocalInfo where togoid = etogo.dataid) )) * cos( radians( (select lng from ETogoLocalInfo where togoid = etogo.dataid) ) - radians(" + mylng + ") ) + sin( radians(" + mylat + ") ) * sin( radians( (select Lat from ETogoLocalInfo where togoid = etogo.dataid) ) ) ) )) as Distance ";
            field += " ,CASE WHEN( ( CONVERT(varchar(12) , Time1Start, 114 ) < CONVERT(varchar(12) , getdate(), 114 )";
            field += "and CONVERT(varchar(12) , Time1End, 114 ) > CONVERT(varchar(12) , getdate(), 114 )";
            field += ") or  ( CONVERT(varchar(12) , Time2Start, 114 ) < CONVERT(varchar(12) , getdate(), 114 )";
            field += "and CONVERT(varchar(12) , Time2End, 114 ) > CONVERT(varchar(12) , getdate(), 114 )";
            field += ") )THEN 1 ELSE 0 END AS havenew ";

            parameters[1].Value = field;
            parameters[2].Value = "DataID";
            parameters[3].Value = orderName;
            parameters[4].Value = pagesize;
            parameters[5].Value = pageindex;
            parameters[6].Value = orderType;
            parameters[7].Value = strWhere;
            parameters[8].Value = ids;
            using (SqlDataReader dr = SQLHelper.ExecuteReader(CommandType.StoredProcedure, "pageselectpri_togo", parameters))
            {
                while (dr.Read())
                {
                    TogoInfo info = new TogoInfo();
                    info.DataID = HJConvert.ToInt32(dr["DataID"]);
                    info.Picture = HJConvert.ToString(dr["Picture"]);
                    info.TogoName = HJConvert.ToString(dr["TogoName"]);
                    int isonline = HJConvert.ToInt32(dr["havenew"]);

                    if (togostatus == 1 && isonline == 1)
                    {
                        info.Status = 1;
                    }
                    else
                    {
                        info.Status = 0;
                    }
                    string _distance = HJConvert.ToString(dr["Distance"]);

                    if (mylat == "0" || _distance == "")
                    {
                        info.mydistance = "0";
                    }
                    else
                    {
                        info.mydistance = Convert.ToString(Convert.ToInt32(Convert.ToDecimal(_distance) * 1000));
                    }

                    info.Lat = HJConvert.ToString(dr["Lat"]);
                    if (info.Lat == "")
                    {
                        info.Lat = "0";
                    }
                    info.Lng = HJConvert.ToString(dr["Lng"]);
                    if (info.Lng == "")
                    {
                        info.Lng = "0";
                    }

                    infos.Add(info);
                }
            }
            return infos;
        }

        /// <summary>
        /// 获取所有商家的名称和对应的编号
        /// </summary>
        /// <param name="pagesize">每页大小</param>
        /// <param name="pageindex">当前页数</param>
        /// <param name="strWhere">搜索条件</param>
        /// <param name="orderName">排序字段</param>
        /// <param name="orderType">排序类型</param>
        /// <param name="mylat">用户纬度</param>
        /// <param name="mylng">用户经度</param>
        public string getDistancetogoid(int pagesize, int pageindex, string sqlwhere, string sortname, int ordertype, string mylat, string mylng)
        {
            string ids = "";
            string orderstr = ordertype > 0 ? "desc" : "asc";
            int top = (pageindex - 1) * pagesize;
            string field = "select top " + top + " dataid";
            field += ",(( 6371 * acos( cos( radians(" + mylat + ") ) * cos( radians( (select Lat from ETogoLocalInfo where togoid = etogo.dataid) )) * cos( radians( (select lng from ETogoLocalInfo where togoid = etogo.dataid) ) - radians(" + mylng + ") ) + sin( radians(" + mylat + ") ) * sin( radians( (select Lat from ETogoLocalInfo where togoid = etogo.dataid) ) ) ) )) as Distance ";
            field += " from etogo where " + sqlwhere + " order by " + sortname + " " + orderstr + " , dataid desc";
            using (SqlDataReader dr = SQLHelper.ExecuteReader(CommandType.Text, field, null))
            {
                while (dr.Read())
                {
                    ids += dr["dataid"] + ",";
                }
            }
            ids = System.Text.RegularExpressions.Regex.Replace(ids, @",$", "");
            return ids;
        }

 以下是代码中用到的分页存储过程:pageselectpri_togo

  分页存储过程
CREATE   PROCEDURE [dbo].[pageselectpri_togo]

@tblName varchar(255),       -- 表名

@strGetFields varchar(1000) = '*',  -- 需要返回的列 

@primary varchar(255)='',      -- 主键的字段名

@orderName varchar(255)='',        --要排序的字段名

@PageSize   int = 10,          -- 页尺寸

@PageIndex  int = 1,           -- 页码

@OrderType bit = 0,  -- 设置排序类型, 非 0 值则降序

@strWhere  varchar(1500) = '',  -- 查询条件 (注意: 不要加 where)
@ids varchar(2000) 

AS

declare @strSQL   varchar(5000)       -- 主语句

declare @strTmp   varchar(110)        -- 临时变量

declare @strOrder varchar(400)        -- 排序类型



if @OrderType != 0
    
begin
    
    set @strTmp = ' not in (select '
    
    set @strOrder = ' order by ' + @orderName +' desc'
    
    if @orderName <> @primary
    begin
        set @strOrder = @strOrder + ',[' + @primary +'] desc'
    end
    --如果@OrderType不是0,就执行降序,这句很重要!
    
end
    
else
    
begin
    
    set @strTmp = ' not in (select '
    
    set @strOrder = ' order by ' + @orderName +' asc'

    if @orderName <> @primary
    begin
        set @strOrder = @strOrder + ',[' + @primary +'] asc'
    end

end

 

if @PageIndex = 1

begin

    if @strWhere != ''   

    set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ '  from [' + @tblName + '] where ' + @strWhere + ' ' + @strOrder
    
     else

     set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ '  from ['+ @tblName + '] '+ @strOrder

--如果是第一页就执行以上代码,这样会加快执行速度

end

else--后面的页数

begin

--以下代码赋予了@strSQL以真正执行的SQL代码

set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ '  from ['

    + @tblName + '] where [' + @primary + ']' + @strTmp + '['+ @primary + '] from (select top ' 
    
    + str((@PageIndex-1)*@PageSize) + ' ['+ @primary + '] from [' + @tblName + ']) as tblTmp)'+ @strOrder

 

if @strWhere != ''

    set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ '  from ['

        + @tblName + '] where [' + @primary + '] not in ( '+@ids+' ) and ' + @strWhere + ' ' + @strOrder

end 


exec (@strSQL)

    此方案用时基本没有问题,但是就是太麻烦了,一个不留神,sql语句就拼错了。

2011年方案

   从那时实现了之前的方案后,后面的项目都沿用,每每用起时,心中总会不痛快,于是开始经常关注这个,一次机会看到了下面的代码(之前一篇博客中看到的,已太久了,记不起出处了,哪位看到别介意),这个方案中也是大量拼接sql,很容易就出错了,另外,对最后一页还要做特别的处理,只因可以用一个方法(2010年方案要用两个方法,查两次数据库)实现,就放到项目中了,后来,经常有重复的商家,或者有些未显示出来,于是部分项目又用了2010年的方案,那个是麻烦,但至少没有错误。

  dal层代码
/// <summary>
        /// 获取列表,返回距离排序
        /// </summary>
        /// <param name="pagesize">每页大小</param>
        /// <param name="pageindex">当前页数</param>
        /// <param name="strWhere">搜索条件</param>
        /// <param name="orderName">排序名称</param>
        /// <param name="orderType">排序类型</param>
        /// <param name="mylat">用户纬度</param>
        /// <param name="mylng">用户经度</param>
        /// <returns></returns>
        public IList<TogoInfo> GetDistanceList(int pagesize, int pageindex, string strWhere, string orderName, int orderType, string mylat, string mylng)
        {
            IList<TogoInfo> infos = new List<TogoInfo>();

            StringBuilder sb = new StringBuilder();

            string distancepstr = "(( 6371 * acos( cos( radians(" + mylat + ") ) * cos( radians( (b.lat) )) * cos( radians( (b.lng) ) - radians(" + mylng + ") ) + sin( radians(" + mylat + ") ) * sin( radians( ( b.Lat) ) ) ) )) as Distance ";
            int endrow = pagesize * pageindex;

            string ordertype = orderType == 1 ? "desc" : "asc";
            string _ordertype = orderType == 0 ? "desc" : "asc";

            sb.Append("select a.* ,b.* ,");
            sb.Append(distancepstr);
            string field = " CASE WHEN( ( CONVERT(varchar(12) , Time1Start, 114 ) < CONVERT(varchar(12) , getdate(), 114 )";
            field += "and CONVERT(varchar(12) , Time1End, 114 ) > CONVERT(varchar(12) , getdate(), 114 )";
            field += ") or  ( CONVERT(varchar(12) , Time2Start, 114 ) < CONVERT(varchar(12) , getdate(), 114 )";
            field += "and CONVERT(varchar(12) , Time2End, 114 ) > CONVERT(varchar(12) , getdate(), 114 )";
            field += ") )THEN 1 ELSE 0 END AS havenew";
            sb.Append(" , "+ field);
            sb.Append(" from etogo as a left join  ETogoLocalInfo as b  on a.dataid = b.togoid ");
            sb.Append("  where  a.dataid in ");
            sb.Append(" ( select top " + pagesize + " dataid from  ");
            sb.Append(" (select top " + endrow + " a.dataid , " + distancepstr + ",sortnum  from  etogo as a left join  ETogoLocalInfo as b  on a.dataid = b.togoid where " + strWhere);
            sb.Append("  order by  " + orderName + " " + ordertype + "" + " ,a.dataid  desc ) as mytepm ");
            sb.Append("  order by  " + orderName + " " + _ordertype + "" + ", dataid asc )");
            sb.Append(" order by  " + orderName + " " + ordertype + "" + ",    a.dataid desc ");

            //Hangjing.Common.HJlog.toLog(sb.ToString());

            using (SqlDataReader dr = SQLHelper.ExecuteReader(CommandType.Text,sb.ToString(), null))
            {
                while (dr.Read())
                {
                    TogoInfo info = new TogoInfo();
                    info.DataID = HJConvert.ToInt32(dr["DataID"]);
                    info.Picture = HJConvert.ToString(dr["Picture"]);
                    info.TogoName = HJConvert.ToString(dr["TogoName"]);
                    int togostatus = HJConvert.ToInt32(dr["Status"]);
                    int isonline = HJConvert.ToInt32(dr["havenew"]);

                    if (togostatus == 1 && isonline == 1)
                    {
                        info.Status = 1;
                    }
                    else
                    {
                        info.Status = 0;
                    }
                    info.mydistance = HJConvert.ToString(dr["Distance"]);

                    if (info.mydistance == "")
                    {
                        info.mydistance = "-1";
                    }
                    info.Lat = HJConvert.ToString(dr["Lat"]);
                    info.Lng = HJConvert.ToString(dr["Lng"]);

                    infos.Add(info);
                }
            }
            return infos;
        }

 

2013年方案

       终于有一次再也忍受不了了,于是下定决心要优化(当然,就我目前的水平,想到的更多还是方便书写)了,当前就想了一点,不在程序中拼接距离的sql语句。经过多次修改,于是有了下面的代码:

  存储过程
-- =============================================
-- Author:        jijunjian
-- Create date: 2013-5-7
-- Description:    获取商家列表(含坐标,按距离排序)
-- 调用 EXEC ETogo_GetShopListWithDistance 10,1,'distance','asc','1=1','30.313035','120.390998','distance<1'
-- =============================================
CREATE PROCEDURE [dbo].[ETogo_GetShopListWithDistance]
@pagesize int,          --分页大小
@pageindex int,         --页码
@orderfield varchar(20),--排序字段名称
@ordertype varchar(5),  --排序类别 desc asc
@where varchar(2000),     --查询条件
@lat VARCHAR(50),--用户纬度
@lng VARCHAR(50),--用户经度,
@otherwhere VARCHAR(2000)--这个条件是用来判断距离,及根据营业时间的状态条件
AS
DECLARE @startRow int,
        @endRow    int,
        @sql varchar(4000),
        @ordername varchar(200)--排序字段


SET    @ordername = '( 6371 * acos( cos( radians(' + @lat + ') ) * cos( radians( Lat )) * cos( radians( lng ) - radians(' + @lng + ') ) + sin( radians(' + @lat + ') ) * sin( radians( Lat ) ) ) )'

  
IF @otherwhere = ''
    SET @otherwhere = '1=1'

SET @startRow = (@pageindex - 1) * @pagesize + 1
SET @endRow = @startRow + @pagesize - 1
SET @sql = '
  SELECT * FROM
  (
    select *,row_number() over(order by  '+@orderfield+' '+@ordertype+') as [rowid] from
    (    
        select '+@ordername+' as distance,dbo.ETogo.*,ETogoLocalInfo.Lat,ETogoLocalInfo.Lng,  
        
        CASE WHEN( ( CONVERT(varchar(12) , Time1Start, 114 ) < CONVERT(varchar(12) , getdate(), 114 )
                and CONVERT(varchar(12) , Time1End, 114 ) > CONVERT(varchar(12) , getdate(), 114 )
                ) or  ( CONVERT(varchar(12) , Time2Start, 114 ) < CONVERT(varchar(12) , getdate(), 114 )
                and CONVERT(varchar(12) , Time2End, 114 ) > CONVERT(varchar(12) , getdate(), 114 )
                ) )THEN 1 ELSE 0 END AS havenew
        
        from  etogo
        LEFT JOIN ETogoLocalInfo ON etogo.dataid=ETogoLocalInfo.togoid WHERE  '+ @where +' 
    ) m where '+ @otherwhere +' 
    
  )m2
  
 WHERE ROWID BETWEEN '+convert(varchar(5), @startRow) +' AND '+ convert(varchar(5), @endRow)+ ' 
'
print @sql
EXEC(@sql)

 

   这个方案让我们搜索N公里内的代码变得简单了 :@otherwhere 参数 设置成 :distance < n 即可了。按距离排序只用:@orderfield=‘distance’就可以了。程序中再也不用出现距离两点距离的sql语句,另一个意外收获就是让我搜索营业中的商家也变得简单了(我们营业根据商家设置的两个时间段(如:8:00-12:00 16:00-20:00)及一个状态值而定).

  以上便是此问题这几年的优化历程,当然,可能很多人对我们行业不了解,也可能我只是抽出了代码片段,很多人看了可能还是会不知所云,不过我想真正想研究这个问题的人看了,就应该能明白了。2013年的方案,可能还有不少问题,或者可以再进一步优化。希望有部分人能用到的同时 ,也能对此方案提出更多更好的意见或建议。

 

  成为一名优秀的程序员!

 

    

   

版权声明:
作者:戢俊建
编辑:妞妞
出处:http://www.cnblogs.com/jijunjian/
本文版权归作者和博客园共有,欢迎转载,大家好,才是真的好!

自己封装的MyNPOI,轻松实现可定制的Excel报表数据导出

NPOI 是个很好的做报表Excel导出导入的开源项目,导出时,服务器不用装任何office,基于NPOI 2.0封装一个壳

本人无聊将其封装,暂时只封装了一类报表定制导出,暂且订版本号 v1.0,My是我的,茗洋的拼音首字母

源码暂时先不公开, 相关类库下载: 开始前往下载

MyNPOI(茗洋NPOI采用表头与表身生成分离的思想,本来想在json中加个field属性,结果太死板了,会有很多功能束缚和性能的损失)

预览版:

v 1.0 preview:

暂时不支持自定义路径指示,默认生成都是在C盘,作为测试体验

支持 自定义JSON 定义表头信息,凡是还有默认的,JSON可不定义,采取默认值方式设置

  一些json,表头单元格属性说明:

     必要项:

    title:单元格要显示的文字,必填

    cellregion:合并单元格的位置,索引从0开始。(fromRow,toRow,fromColumn,toColumn)

                          例如 ‘cellregion’: ‘1,3,2,4’,就是从第二行到第4行行合并,然后从第二行第三列开始到第二行的的第五列合并结束

                                   ‘cellregion’:‘0,0,0,0’ 这个合并后的效果肯定是第一行第一列,也相当于没合并

    可选项:

    align:单元格水平显示方式,默认:居中,可用的值有left,center,right

    valign:单元格垂直显示方式,默认:居中,可用的值有 top,center,bottom

    bgcolor: 单元格背景颜色,支持16进制,例如#ffffff,默认 灰色

    width: 单元格宽度(列宽),在 多行合并时候,宽度设置可能有问题,但是通过全局的 defaultwidth:可修复,暂未解决,可有可无

                   但是单行表头的当然没事,采取defaultwidth方式,是平均宽度

    height: 单元格的高度,也就是行高,也可以使用全局的defaultheight,定义个全局的相等高度

    IsItalic:是否是斜体,默认 false

    IsStrikeout:是否有中间线,默认 false

    Underline:是否具有下划线,默认 false

    fontName:字体名称,例如 “微软雅黑”,默认“黑体”

    fontcolor:单元格字体颜色,支持16进制,例如#ffffff,默认 黑色

    fontsize:单元格字体大小,默认12,

    关于fontweight还未公开,暂时写死的默认400

     

关于表头的一些属性:

defaultwidth: 如果单元格内没有设置width,将统一宽度(列宽)设置

defaultheight:如果单元格内没有设置height,将统一高度(行宽)设置

rowspan: 表头一共占用了多少行,暂时必填选项,普通的一行就是1

sheetname:excel中sheet的名称

 

下个版本v1.0 真实 功能:

支持WEB导出,自定义名称,自动下载,桌面端自定义地址导出

border设置,由于合并后显示的border会有问题,暂时尚未实现

下个版本正式发布会 类库面向大众,但是现在只是基本功能演示版本

 

 

v1.2版本

   JSON表头生成助手,例如:Head.Add("一个json单元格子项").Add("一个json单元格子项")…

 

V2.0版本

支持Excel导入变成List<T>类型的处理

根据某列分组导出 Excel,内容里面某列合并的效果

 

V2.2版本

不定数量的列的 导出Excel解决方案导出

 

V3.0版本

Json支持自定义列统计,某列(指定位置)的和,平均数,最小值,最大值

 

 

后续….

 

 

============================================================

使用方法,新建项目,直接引用 MyNPOI.dll

下载我搭好的空格子demo          下载

大致壳这样子:

排序、搜索_第1张图片

一级表头JSON示例

private string TestCommomJson = @"{ 'root':{'rowspan':1,'sheetname':'按商机类型分类','defaultwidth':12,'defaultheight':55,'head':[ {
                                                                      'title':'排序',
                                                                      'align':'center',
                                                                      'bgcolor':'#DE0A0A',
                                                                      'fontsize':14,
                                                                      'fontcolor':'#ffffff',
                                                                      'cellregion':'0,0,0,0',
                                                                      'width':20
                                                                   },
                                                                  {
                                                                      'title':'统计日期',
                                                                      'align':'center',
                                                                       'bgcolor':'#2556C9',
                                                                      'fontsize':14,
                                                                      'fontcolor':'#C96725',
                                                                      'cellregion':'0,0,1,1',
                                                                      'width':80
                                                                 },
                                                                    {
                                                                      'title':'后台预约',
                                                                      'align':'center',
                                                                      'fontsize':14,
                                                                      'cellregion':'0,0,2,2',
                                                                      'width':50
                                                                 }, {
                                                                      'title':'在线咨询',
                                                                      'align':'center',
                                                                      'fontsize':14,
                                                                      'cellregion':'0,0,3,3',
                                                                      'width':50
                                                                 }, {
                                                                      'title':'电话咨询',
                                                                      'align':'center',
                                                                      'fontsize':14,
                                                                      'cellregion':'0,0,4,4',
                                                                      'width':50
                                                                 }
                                                                    ]
                                                                }
                                                               }";

二级商机JSON示例

  private string TestAdvanceJson = @"{ 'root':{'rowspan':2,'sheetname':'按商机类型分类','defaultwidth':12,'defaultheight':35,'head':[ {
                                                                      'title':'排序',
                                                                      'align':'center',
                                                                      'bgcolor':'#820C5F',
                                                                      'fontsize':14,
                                                                      'fontcolor':'#FBEC00',
                                                                      'cellregion':'0,1,0,0',
                                                                      'width':30
                                                                   },
                                                                  {
                                                                      'title':'统计日期',
                                                                      'align':'center',
                                                                      'fontsize':14,
                                                                      'cellregion':'0,1,1,1',
                                                                      'width':30
                                                                 },
                                                                   {
                                                                      'title':'商机阶段',
                                                                      'align':'center',
                                                                      'fontsize':14,
                                                                      'width':60,
                                                                      'cellregion':'0,0,2,7'
                                                                 },
                                                                  {
                                                                      'title':'商机关闭',
                                                                      'align':'center',
                                                                      'fontsize':14,
                                                                      'cellregion':'0,0,8,10'
                                                                 },
                                                                    {
                                                                      'title':'合计',
                                                                      'align':'right',
                                                                      'cellregion':'0,1,11,11',
                                                                      'bgcolor':'#FFFFFF',
                                                                      'fontsize':14,
                                                                      'fontcolor':'#DF0B0B'
                                                                 },
                                                                    {
                                                                      'title':'挖掘商机',
                                                                      'align':'center',
                                                                      'fontsize':12,
                                                                      'cellregion':'1,1,2,2'
                                                                 },{
                                                                      'title':'获取认同',
                                                                      'align':'center',
                                                                      'fontsize':12,
                                                                      'cellregion':'1,1,3,3'
                                                                 },{
                                                                      'title':'方案制定',
                                                                      'align':'center',
                                                                      'fontsize':12,
                                                                      'cellregion':'1,1,4,4',
                                                                 },{
                                                                      'title':'问题处理',
                                                                      'align':'center',
                                                                      'fontsize':12,
                                                                      'cellregion':'1,1,5,5'
                                                                 },{
                                                                      'title':'客户促成',
                                                                      'align':'center',
                                                                      'width':10,
                                                                      'fontsize':12,
                                                                      'cellregion':'1,1,6,6'
                                                                 },{
                                                                      'title':'签单处理',
                                                                      'align':'center',
                                                                      'fontsize':12,
                                                                      'cellregion':'1,1,7,7'
                                                                 }
                                                               ]}
                                                               }";

三级表头JSON示例

    private string TestMoreAdvanceJson = @"{ 'root':{'rowspan':3,'sheetname':'按商机类型分类','defaultwidth':12,'defaultheight':35,'head':[ {
                                                                      'title':'排序',
                                                                      'bgcolor':'#FBEC00',
                                                                      'fontsize':14,
                                                                      'fontcolor':'#ffffff',
                                                                      'cellregion':'0,2,0,0',
                                                                      'width':50
                                                                   },
                                                                  {
                                                                      'title':'统计日期',
                                                                      'fontsize':14,
                                                                      'cellregion':'0,2,1,1',
                                                                      'width':50
                                                                 },
                                                                   {
                                                                      'title':'商机阶段',
                                                                      'fontsize':14,
                                                                      'cellregion':'0,0,2,7'
                   
                                                                 },{
                                                                      'title':'商机初级阶段',
                                                                      'fontsize':14,
                                                                      'cellregion':'1,1,2,4'
                                                                 },{
                                                                      'title':'商机高级阶段',
                                                                      'fontsize':14,
                                                                      'cellregion':'1,1,5,7'
                                                                 },
                                                                  {
                                                                      'title':'商机关闭',
                                                                      'fontsize':14,
                                                                      'cellregion':'0,0,8,10'
                                                                 },
                                                                  {
                                                                      'title':'无法促成',
                                                                      'fontsize':12,
                                                                      'fontName':'隶书',
                                                                      'cellregion':'1,2,8,8'
                                                                 },
                                                                    {
                                                                      'title':'重复商机',
                                                                      'fontsize':12,
                                                                      'cellregion':'1,2,9,9'
                                                                 },
                                                                 {
                                                                      'title':'无效商机',
                                                                      'fontsize':12,
                                                                      'cellregion':'1,2,10,10'
                                                                  },
                                                                    {
                                                                      'title':'合计',
                                                                      'fontsize':'14',
                                                                      'cellregion':'0,2,11,11'
                                                                 },
                                                                    {
                                                                      'title':'A',
                                                                      'fontsize':14,
                                                                      'cellregion':'2,2,2,2'
                                                                 },{
                                                                      'title':'B',
                                                                      'fontsize':14,
                                                                      'cellregion':'2,2,3,3'
                                                                 },{
                                                                      'title':'C',
                                                                      'cellregion':'2,2,4,4'
                                                                 },{
                                                                      'title':'D',
                                                                      'cellregion':'2,2,5,5'
                                                                 },{
                                                                      'title':'E',
                                                                      'cellregion':'2,2,6,6'
                                                                 },{
                                                                      'title':'F',
                                                                      'cellregion':'2,2,7,7'
                                                                 }
                                                               ]}
                                                               }";

关于这3个示例的JSON中有cellregion的数字你可能不确定

首先我们要在表格里面画出基本的位置图,然后合并等问题才好写,如果你的上面的数字自己会标了,你的cellregion才不会设置错

排序、搜索_第2张图片

这里我局二级表头的例子

二级表头例子的  要显示的第一列 合并了第一第二行,以左上角的位置,也就是第一行,第一列,列的索引是0,所以这里就是0

同理1,然后就是第一行合并三个,根据,0,1,然后下个是2,所以是2,

image

继续写,同行的就是3,4,5,6,7,所以上面一行表面是2,但是它跨了6格到7,所以他的下一个合并就是8,同理标示数字

关于cellregion的设置理解起来还是有难度的

原生NPOI也是这种合并方式处理的

 

定义好这3个测试json,我们把他们和对应的按钮绑定,单击了,就把json放到文本框中显示

排序、搜索_第3张图片

 

 

①根据json导出表头

先引用  using MyNPOI.Excel;

光导出表头 ,第一个参数是导出的文件名,第二个必须设置true,才能单独导出表头,这里只是测试用,真正发布,我会取消此方法

            ExcelHelper ext = new ExcelHelper(null, true);

            ext.SetHead(txtJsonHead.Text); 
            System.Diagnostics.Process.Start("c:\\");

调用SetHead生成表头信息,导出后成功后打开C盘,此版本默认生成的excel都在c盘根目录,第一个文件名暂时无用,只是测试

关于SetHead中的单独导出表头是

的相关代码:

    FileStream file = new FileStream(@"c:\test" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".xls", FileMode.Create);

所以是test随机数.xls

所以你在new ExcelHelper的时候设置了文件名没有任何作用。

 

运行效果:

排序、搜索_第4张图片

image

打开Excel,这里我在json中设置背景颜色,字体颜色等等

排序、搜索_第5张图片

整体表头演示

 排序、搜索_第6张图片

 

关于表身演示

接下来我们创建一个类StatisticOpSourceDto.cs,这里面全是属性

 

using System;
using System.Collections;
 
namespace TestMyNPOI
{
    public class StatisticOpSourceDto
    {
        public int SortId { get; set; }
        public DateTime StatisticDate
        {
            get;
            set;
        }
 
        public int DHZX
        {
            get;
            set;
        }
        public int ZXZX
        {
            get;
            set;
        }
        public int HTYY
        {
            get;
            set;
        }
    }
}

这里我们手动创建一个数据源,这里我们的数据可以从后台传来,暂时先List<T>型的,但是表内的数据结构不要太复杂,目前没有测试负责的数据

未来支持更多的数据源格式,暂时先List<T>

 

   1:          #region 测试的集合体
   2:          public List<StatisticOpSourceDto> dtp = new List<StatisticOpSourceDto> { 
   3:             new StatisticOpSourceDto{SortId=1,StatisticDate=Convert.ToDateTime("2013-05-08"),HTYY=44,ZXZX=5,DHZX=6},
   4:            new StatisticOpSourceDto{SortId=2,StatisticDate=Convert.ToDateTime("2013-05-09"),HTYY=144,ZXZX=45,DHZX=664},
   5:            new StatisticOpSourceDto{SortId=3,StatisticDate=Convert.ToDateTime("2013-05-08"),HTYY=434,ZXZX=578,DHZX=56}
   6:          };
   7:          public string[] titles = { "SortId", "StatisticDate", "HTYY", "ZXZX", "DHZX" }; //测试完全用的
   8:          public string[] titles2 = { "SortId", "StatisticDate", "HTYY", "ZXZX"};  //测试 不完全用的
   9:          #endregion

titles是 绑定T里面的属性的名称,不要搞错了,必须一一对应

我们再添加一个按钮,这里我们StatisticOpSourceDto有5个属性,都赋值了,“完全”那个按钮是 5列都显示,“不完全”就是指定列显示

image

完全的代码如下:就是titles和titles2,两个参数不一样,还有linq后的列顺序不一样

这里暂时强烈要求,titles的 字符串 顺序,必须与后面最后一个参数的 linq,捕获的列要一一对应,后期会优化

完全的代码如下:

   ExcelHelper ext = new ExcelHelper();
            ext.ExportToExcel<StatisticOpSourceDto>(dtp, "20130522报表测试" + DateTime.Now.ToString("mmssfff"), titles,  txtJsonHead.Text,
                   a => a.SortId.ToString(),
                   c => c.StatisticDate.ToString("yyyy-MM-dd"),
                   a => a.HTYY.ToString(),
                   a => a.ZXZX.ToString(),
                   a => a.DHZX.ToString()
                   );

不完全的代码如下:

    ExcelHelper ext = new ExcelHelper();
            ext.ExportToExcel<StatisticOpSourceDto>(dtp, "20130523报表测试" + DateTime.Now.ToString("mmssfff"), titles2,  txtJsonHead.Text,
              a => a.SortId.ToString(),
              c => c.StatisticDate.ToString("yyyy-MM-dd"),
              a => a.HTYY.ToString(),
              a => a.ZXZX.ToString()
              );

 

这里暂时也是定死在C盘,只需一个文件名,不需要后缀名,下个版本正式发布会 面向大众 
效果图:

 

整体上,表现表头与表身分离的思想,更加灵活,如果绑定,程序就要加很多判断代码,会有性能损失,且耦合性太高,因为json很容易写错,而不容易发现

关于后期,会提供一个表头生成辅助类

 

 

以上是暂时这样的解决方案的一个封装。关于源码,暂时先不要我问我要的,写个代码不容易,至于稳定的时候,会将源码公开,让MyNPOI(茗洋NPOI)更加好用。

 

==========================防止别人粘贴复制,茗洋芳竹地址:www.cnblogs.com/fresh-air=======

 

如果你喜欢这样的封装,我会继续下去。欢迎推荐,我是茗洋,原名杨洋

谢绝转载,茗洋原创,写个文章不容易。

6月1日发布,基本大众版本v1.0,到时可以实现web项目的复杂表头excel导出,导出下载,导出文件存储到服务器的指定位置

本项目,我自己一个人编写,有兴趣的可以交流

 

相关下载:

已经完成的Demo演示下载:

点击开始下载               更新日期 2013年5月23日1:21:55

 
 
分类:  ASPNET MVC4

你可能感兴趣的:(排序)