Data序列——盘点几种数据库的分页SQL的写法

      使用SQL分页查询,能够有效的提高数据库查询的效率,以及返回客户端数据的最小化。

      几种数据库的分页SQL都不一样,SQLite和MySql的比较相近,而Oracle稍稍要绕一点,MsSql的最难处理。

 

一、SQLite和MySql分页

      SQLite和MySql使用Limit和Offset关键字就能够实现分页。

        /// <summary>
        /// 对命令文本进行分段处理,使之能够返回小范围内的数据。
        /// </summary>
        /// <param name="commandText">命令文本。</param>
        /// <param name="segment">数据分段对象。</param>
        /// <returns>处理后的分段命令文本。</returns>
        /// <exception cref="SegmentNotSupportedException">当前数据库或版本不支持分段时,引发该异常。</exception>
        public virtual string Segment(string commandText, IDataSegment segment)
        {
            commandText = string.Format(@"
                SELECT T.* FROM 
                (
                    {0}
                ) T LIMIT {1}{2}",
                commandText,
                segment.Length != 0 ? segment.Length : 1000,
                segment.Start != null ? " OFFSET " + (segment.Start - 1) : "");
            return commandText;
        }

 

二、Oracle分页

      Oracle的分页是有技巧的,比如以前是这样写的:

        /// <summary>
        /// 对命令文本进行分段处理,使之能够返回小范围内的数据。
        /// </summary>
        /// <param name="commandText">命令文本。</param>
        /// <param name="segment">数据分段对象。</param>
        /// <returns>处理后的分段命令文本。</returns>
        /// <exception cref="SegmentNotSupportedException">当前数据库或版本不支持分段时,引发该异常。</exception>
        public virtual string Segment(string commandText, IDataSegment segment)
        {
            commandText = string.Format(@"
                SELECT T.* FROM
                (
                    SELECT T.*, ROWNUM ROW_NUM
                    FROM ({0}) T
                ) T
                WHERE {1}",
                commandText, segment.Condition("ROW_NUM"));
            return commandText;
        }

      以上的segment.Condition是对ROW_NUM进行Between拼装:

        /// <summary>
        /// 构造 <see cref="IDataSegment"/> 的分页条件。
        /// </summary>
        /// <param name="segment"></param>
        /// <param name="fieldName">分页列名称。</param>
        /// <returns></returns>
        internal static string Condition(this IDataSegment segment, string fieldName)
        {
            if (segment.Start != null && segment.End != null)
            {
                return string.Format("{0} BETWEEN {1} AND {2}", fieldName, segment.Start, segment.End);
            }
            if (segment.Start != null && segment.End == null)
            {
                return string.Format("{0} >= {1}", fieldName, segment.Start);
            }
            if (segment.Start == null && segment.End != null)
            {
                return string.Format("{0} <= {1}", fieldName, segment.End);
            }
            return "1 = 1";
        }

      最近在分析Oracle分页语句的时候,发现,Oracle的分页在外面嵌套了两层,如果将ROW_NUM的小于条件提进一层,则查询的速度将会提高90%左右。修改分页如下:

        /// <summary>
        /// 对命令文本进行分段处理,使之能够返回小范围内的数据。
        /// </summary>
        /// <param name="commandText">命令文本。</param>
        /// <param name="segment">数据分段对象。</param>
        /// <returns>处理后的分段命令文本。</returns>
        /// <exception cref="SegmentNotSupportedException">当前数据库或版本不支持分段时,引发该异常。</exception>
        public virtual string Segment(string commandText, IDataSegment segment)
        {
            //** rownnum <= n 放在内层能够提高10倍的速度!
            commandText = string.Format(@"
                SELECT T.* FROM
                (
                    SELECT T.*, ROWNUM ROW_NUM
                    FROM ({0}) T {1}
                ) T {2}", 
                    commandText, segment.End != null ? "WHERE ROWNUM <= " + segment.End : string.Empty,
                    segment.Start != null ? "WHERE ROW_NUM >= " + segment.Start: string.Empty);
            return commandText;
        }

      这个优化各位可以去百度相关的介绍,这里就不再细说了。

 

三、MsSql分页

      MsSql在2005版本以前是不支持分页语句的,只有写个存储过程来使用几个top嵌套来实现分页,从2005开始,MsSql也提供了ROW_NUMBER() OVER()这样的分页关键字,但是处理还是很麻烦,因为SQL中的排序字段必须提到OVER中来。

        /// <summary>
        /// 对命令文本进行分段处理,使之能够返回小范围内的数据。
        /// </summary>
        /// <param name="commandText">命令文本。</param>
        /// <param name="segment">数据分段对象。</param>
        /// <returns>处理后的分段命令文本。</returns>
        /// <exception cref="SegmentNotSupportedException">当前数据库或版本不支持分段时,引发该异常。</exception>
        public virtual string Segment(string commandText, IDataSegment segment)
        {
            var regxOrder = new Regex(@"order\s+by ([\W|\w])+", RegexOptions.IgnoreCase);
            var regAlias = new Regex(@"(\S+)?\.");
            //如果有排序
            if (regxOrder.IsMatch(commandText))
            {
                var matchs = regxOrder.Matches(commandText);
                //去除子句中的Order并移到OVER后
                commandText = string.Format(@"
                    SELECTT.* FROM 
                    (
                        SELECT T.*, ROW_NUMBER() OVER ({2}) AS ROW_NUM 
                        FROM ({0}) T
                    ) T 
                    WHERE {1}",
                    regxOrder.Replace(commandText, "").Trim(),
                    segment.Condition("ROW_NUM"),
                    regAlias.Replace(matchs[matchs.Count - 1].Value, ""));
            }
            else
            {
                commandText = string.Format(@"
                    SELECT T.* FROM 
                    (
                        SELECT T.*, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ROW_NUM 
                        FROM ({0}) T
                    ) T 
                    WHERE {1}",
                    commandText, segment.Condition("ROW_NUM"));
            }
            return commandText;
        }

 

你可能感兴趣的:(Data)