Exemple #1
0
        /// <summary>分页算法</summary>
        /// <remarks>
        /// builder里面必须含有排序,否则就要通过key指定主键,否则大部分算法不能使用,会导致逻辑数据排序不正确。
        /// 其实,一般数据都是按照聚集索引排序,而聚集索引刚好也就是主键。
        /// 所以,只要设置的Key顺序跟主键顺序一致,就没有问题。
        /// 如果,Key指定了跟主键不一致的顺序,那么查询语句一定要指定同样的排序。
        /// </remarks>
        /// <param name="builder"></param>
        /// <param name="startRowIndex"></param>
        /// <param name="maximumRows"></param>
        /// <param name="isSql2005"></param>
        /// <param name="queryCountCallback">查询总记录数的委托,近供DoubleTop使用</param>
        /// <returns></returns>
        public static SelectBuilder PageSplit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows, Boolean isSql2005, Func<SelectBuilder, Int64> queryCountCallback = null)
        {
            // 从第一行开始,不需要分页
            if (startRowIndex <= 0)
            {
                if (maximumRows < 1)
                    return builder;
                else
                    return builder.Clone().Top(maximumRows);
            }

            if (builder.Keys == null || builder.Keys.Length < 1) throw new XCodeException("分页算法要求指定排序列!" + builder.ToString());

            // 其实,一般数据都是按照聚集索引排序,而聚集索引刚好也就是主键
            // 所以,只要设置的Key顺序跟主键顺序一致,就没有问题
            // 如果,Key指定了跟主键不一致的顺序,那么查询语句一定要指定同样的排序

            //// 如果不指定排序,只能使用TopNotIn,另外三种都会影响结果的顺序
            //if (String.IsNullOrEmpty(builder.OrderBy))
            //{
            //    // 取前面页(经试验Access字符串主键大概在200行以下)并且没有排序的时候,TopNotIn算法应该是最快的
            //    if (startRowIndex < 200) return TopNotIn(builder, startRowIndex, maximumRows);
            //}

            if (isSql2005) return RowNumber(builder, startRowIndex, maximumRows);

            // 必须有排序,且排序字段必须就是数字主键
            if (builder.IsInt && builder.KeyIsOrderBy) return MaxMin(builder, startRowIndex, maximumRows);

            if (maximumRows > 0) return DoubleTop(builder, startRowIndex, maximumRows, queryCountCallback);

            return TopNotIn(builder, startRowIndex, maximumRows);
        }
Exemple #2
0
        /// <summary>最经典的NotIn分页,通用但是效率最差。只需指定一个排序列。</summary>
        /// <param name="builder">查询生成器</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <returns>分页SQL</returns>
        static SelectBuilder TopNotIn(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
        {
            if (builder.Keys == null || builder.Keys.Length != 1) throw new ArgumentNullException("Key", "TopNotIn分页算法要求指定单一主键列!" + builder.ToString());

            // 分页标准 Select (20,10,ID)
            // 1,取目标页之前的20行
            // 2,排除前20行之后取10行
            // Select Top 10 * From Table Where ID Not In(Select Top 20 ID From Table)

            // 构建Select Top 20 ID From Table
            var builder1 = builder.Clone().Top(startRowIndex, builder.Key);

            SelectBuilder builder2 = null;
            if (maximumRows < 1)
                builder2 = builder.CloneWithGroupBy("XCode_T0");
            else
                builder2 = builder.Clone().Top(maximumRows);

            builder2.AppendWhereAnd("{0} Not In({1})", builder.Key, builder1);
            // 结果列处理
            builder2.Column = builder.Column;
            // 如果结果列包含有“.”,即有形如tab1.id、tab2.name之类的列时设为获取子查询的全部列
            if ((!string.IsNullOrEmpty(builder2.Column)) && builder2.Column.Contains("."))
            {
                builder2.Column = "*";
            }

            return builder2;
        }
Exemple #3
0
        /// <summary>分页算法</summary>
        /// <remarks>
        /// builder里面必须含有排序,否则就要通过key指定主键,否则大部分算法不能使用,会导致逻辑数据排序不正确。
        /// 其实,一般数据都是按照聚集索引排序,而聚集索引刚好也就是主键。
        /// 所以,只要设置的Key顺序跟主键顺序一致,就没有问题。
        /// 如果,Key指定了跟主键不一致的顺序,那么查询语句一定要指定同样的排序。
        /// </remarks>
        /// <param name="builder"></param>
        /// <param name="startRowIndex"></param>
        /// <param name="maximumRows"></param>
        /// <param name="isSql2005"></param>
        /// <param name="queryCountCallback">查询总记录数的委托,近供DoubleTop使用</param>
        /// <returns></returns>
        public static SelectBuilder PageSplit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows, Boolean isSql2005, Func<SelectBuilder, Int64> queryCountCallback = null)
        {
            // 从第一行开始,不需要分页
            // 2012.11.08 注释掉首页使用SELECT TOP的方式,此方式在对有重复数据出现的字段排序时,
            // 与Row_Number()的规则不一致,导致出现第一、二页排序出现重复记录。
            // 具体可百度一下【结合TOP N和Row_Number()分页因Order by排序规则不同引起的bug】
            if (startRowIndex <= 0)
            {
                if (maximumRows < 1)
                {
                    return builder;
                }
                else if (builder.KeyIsOrderBy)
                {
                    return builder.Clone().Top(maximumRows);
                }
            }

            if (builder.Keys == null || builder.Keys.Length < 1) throw new XCodeException("分页算法要求指定排序列!" + builder.ToString());

            // 其实,一般数据都是按照聚集索引排序,而聚集索引刚好也就是主键
            // 所以,只要设置的Key顺序跟主键顺序一致,就没有问题
            // 如果,Key指定了跟主键不一致的顺序,那么查询语句一定要指定同样的排序

            //// 如果不指定排序,只能使用TopNotIn,另外三种都会影响结果的顺序
            //if (String.IsNullOrEmpty(builder.OrderBy))
            //{
            //    // 取前面页(经试验Access字符串主键大概在200行以下)并且没有排序的时候,TopNotIn算法应该是最快的
            //    if (startRowIndex < 200) return TopNotIn(builder, startRowIndex, maximumRows);
            //}

            if (isSql2005) return RowNumber(builder, startRowIndex, maximumRows);

            // 必须有排序,且排序字段必须就是数字主键
            if (builder.IsInt && builder.KeyIsOrderBy) return MaxMin(builder, startRowIndex, maximumRows);

            if (maximumRows > 0) return DoubleTop(builder, startRowIndex, maximumRows, queryCountCallback);

            return TopNotIn(builder, startRowIndex, maximumRows);
        }
Exemple #4
0
        /// <summary>最经典的NotIn分页,通用但是效率最差。只需指定一个排序列。</summary>
        /// <param name="builder">查询生成器</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <returns>分页SQL</returns>
        static SelectBuilder TopNotIn(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
        {
            if (builder.Keys == null || builder.Keys.Length != 1) throw new ArgumentNullException("Key", "TopNotIn分页算法要求指定单一主键列!" + builder.ToString());

            // 分页标准 Select (20,10,ID)
            // 1,取目标页之前的20行
            // 2,排除前20行之后取10行
            // Select Top 10 * From Table Where ID Not In(Select Top 20 ID From Table)

            // 构建Select Top 20 ID From Table
            SelectBuilder builder1 = builder.Clone().Top(startRowIndex, builder.Key);

            SelectBuilder builder2 = null;
            if (maximumRows < 1)
                builder2 = builder.CloneWithGroupBy("XCode_T0");
            else
                builder2 = builder.Clone().Top(maximumRows);

            builder2.AppendWhereAnd("{0} Not In({1})", builder.Key, builder1.ToString());

            return builder2;
        }
Exemple #5
0
        /// <summary>克隆</summary>
        /// <returns></returns>
        public SelectBuilder Clone()
        {
            var sb = new SelectBuilder();
            sb.Column = this.Column;
            sb.Table = this.Table;
            // 直接拷贝字段,避免属性set时触发分析代码
            sb._Where = this._Where;
            sb._OrderBy = this._OrderBy;
            sb.GroupBy = this.GroupBy;
            sb.Having = this.Having;

            sb.Keys = this.Keys;
            sb.IsDescs = this.IsDescs;
            sb.IsInt = this.IsInt;

            return sb;
        }
Exemple #6
0
 /// <summary>构造分页SQL</summary>
 /// <param name="builder">查询生成器</param>
 /// <param name="startRowIndex">开始行,0表示第一行</param>
 /// <param name="maximumRows">最大返回行数,0表示所有行</param>
 /// <returns>分页SQL</returns>
 public override SelectBuilder PageSplit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows) => MySql.PageSplitByLimit(builder, startRowIndex, maximumRows);
Exemple #7
0
 public override SelectBuilder PageSplit(SelectBuilder builder, int startRowIndex, int maximumRows)
 {
     return MSPageSplit.PageSplit(builder, startRowIndex, maximumRows, false, b => CreateSession().QueryCount(b));
 }
Exemple #8
0
        /// <summary>按唯一数字最大最小分页,性能很好。必须指定一个数字型排序列。</summary>
        /// <param name="builder">查询生成器</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <returns>分页SQL</returns>
        static SelectBuilder MaxMin(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
        {
            if (builder.Keys == null || builder.Keys.Length != 1) throw new ArgumentNullException("Key", "TopNotIn分页算法要求指定单一主键列!" + builder.ToString());

            // 分页标准 Select (20,10,ID Desc)
            // Select Top 10 * From Table Where ID>(Select max(ID) From (Select Top 20 ID From Table Order By ID) Order By ID Desc) Order By ID Desc

            var builder1 = builder.Clone().Top(startRowIndex, builder.Key);
            var builder2 = builder1.AsChild("XCode_T0");
            builder2.Column = String.Format("{0}({1})", builder.IsDesc ? "Min" : "Max", builder.Key);

            SelectBuilder builder3 = null;
            if (maximumRows < 1)
                builder3 = builder.CloneWithGroupBy("XCode_T1");
            else
                builder3 = builder.Clone().Top(maximumRows);

            // 如果本来有Where字句,加上And,当然,要区分情况按是否有必要加圆括号
            builder3.AppendWhereAnd("{0}{1}({2})", builder.Key, builder.IsDesc ? "<" : ">", builder2);

            return builder3;
        }
Exemple #9
0
        /// <summary>构造分页SQL</summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <param name="keyColumn">唯一键。用于not in分页</param>
        /// <returns>分页SQL</returns>
        public override String PageSplit(String sql, Int32 startRowIndex, Int32 maximumRows, String keyColumn)
        {
            // 从第一行开始,不需要分页
            if (startRowIndex <= 0 && maximumRows < 1) return sql;

            // 指定了起始行,并且是SQL2005及以上版本,使用RowNumber算法
            if (startRowIndex > 0 && IsSQL2005)
            {
                //return PageSplitRowNumber(sql, startRowIndex, maximumRows, keyColumn);
                SelectBuilder builder = new SelectBuilder();
                builder.Parse(sql);
                return MSPageSplit.PageSplit(builder, startRowIndex, maximumRows, IsSQL2005).ToString();
            }

            // 如果没有Order By,直接调用基类方法
            // 先用字符串判断,命中率高,这样可以提高处理效率
            if (!sql.Contains(" Order "))
            {
                if (!sql.ToLower().Contains(" order ")) return base.PageSplit(sql, startRowIndex, maximumRows, keyColumn);
            }
            //// 使用正则进行严格判断。必须包含Order By,并且它右边没有右括号),表明有order by,且不是子查询的,才需要特殊处理
            //MatchCollection ms = Regex.Matches(sql, @"\border\s*by\b([^)]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            //if (ms == null || ms.Count < 1 || ms[0].Index < 1)
            String sql2 = sql;
            String orderBy = CheckOrderClause(ref sql2);
            if (String.IsNullOrEmpty(orderBy))
            {
                return base.PageSplit(sql, startRowIndex, maximumRows, keyColumn);
            }
            // 已确定该sql最外层含有order by,再检查最外层是否有top。因为没有top的order by是不允许作为子查询的
            if (Regex.IsMatch(sql, @"^[^(]+\btop\b", RegexOptions.Compiled | RegexOptions.IgnoreCase))
            {
                return base.PageSplit(sql, startRowIndex, maximumRows, keyColumn);
            }
            //String orderBy = sql.Substring(ms[0].Index);

            // 从第一行开始,不需要分页
            if (startRowIndex <= 0)
            {
                if (maximumRows < 1)
                    return sql;
                else
                    return String.Format("Select Top {0} * From {1} {2}", maximumRows, CheckSimpleSQL(sql2), orderBy);
                //return String.Format("Select Top {0} * From {1} {2}", maximumRows, CheckSimpleSQL(sql.Substring(0, ms[0].Index)), orderBy);
            }

            #region Max/Min分页
            // 如果要使用max/min分页法,首先keyColumn必须有asc或者desc
            String kc = keyColumn.ToLower();
            if (kc.EndsWith(" desc") || kc.EndsWith(" asc") || kc.EndsWith(" unknown"))
            {
                String str = PageSplitMaxMin(sql, startRowIndex, maximumRows, keyColumn);
                if (!String.IsNullOrEmpty(str)) return str;
                keyColumn = keyColumn.Substring(0, keyColumn.IndexOf(" "));
            }
            #endregion

            sql = CheckSimpleSQL(sql2);

            if (String.IsNullOrEmpty(keyColumn)) throw new ArgumentNullException("keyColumn", "分页要求指定主键列或者排序字段!");

            if (maximumRows < 1)
                sql = String.Format("Select * From {1} Where {2} Not In(Select Top {0} {2} From {1} {3}) {3}", startRowIndex, sql, keyColumn, orderBy);
            else
                sql = String.Format("Select Top {0} * From {1} Where {2} Not In(Select Top {3} {2} From {1} {4}) {4}", maximumRows, sql, keyColumn, startRowIndex, orderBy);
            return sql;
        }
Exemple #10
0
 public override SelectBuilder PageSplit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows) => MSPageSplit.PageSplit(builder, startRowIndex, maximumRows, false, b => CreateSession().QueryCount(b));
Exemple #11
0
 /// <summary>构造分页SQL</summary>
 /// <param name="builder">查询生成器</param>
 /// <param name="startRowIndex">开始行,0表示第一行</param>
 /// <param name="maximumRows">最大返回行数,0表示所有行</param>
 /// <returns>分页SQL</returns>
 public override SelectBuilder PageSplit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows) => Server.PageSplit(builder, startRowIndex, maximumRows);
Exemple #12
0
 public override SelectBuilder PageSplit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
 {
     return(MSPageSplit.PageSplit(builder, startRowIndex, maximumRows, IsSQL2005, b => CreateSession().QueryCount(b)));
 }
Exemple #13
0
        /// <summary>同步单表数据</summary>
        /// <remarks>
        /// 把数据同一张表同步到另一个库
        /// </remarks>
        /// <param name="table">数据表</param>
        /// <param name="connName">目标连接名</param>
        /// <param name="syncSchema">同步架构</param>
        /// <param name="progress">进度回调,参数为已处理行数和当前页表</param>
        /// <returns></returns>
        public Int32 Sync(IDataTable table, String connName, Boolean syncSchema = true, Action <Int32, DbTable> progress = null)
        {
            var dal = connName.IsNullOrEmpty() ? null : Create(connName);

            var writeDb = new WriteDbActor
            {
                Table = table,
                Dal   = dal,

                // 最多同时堆积数页
                BoundedCapacity = 4,
            };

            // 自增
            var id = table.Columns.FirstOrDefault(e => e.Identity);

            // 主键
            if (id == null)
            {
                var pks = table.PrimaryKeys;
                if (pks != null && pks.Length == 1 && pks[0].DataType.IsInt())
                {
                    id = pks[0];
                }
            }

            var sw = Stopwatch.StartNew();

            // 表结构
            if (syncSchema)
            {
                dal.SetTables(table);
            }

            var sb = new SelectBuilder
            {
                Table = Db.FormatName(table)
            };

            var row      = 0L;
            var pageSize = (Db as DbBase).BatchSize;
            var total    = 0;

            while (true)
            {
                var sql = "";
                // 分割数据页,自增或分页
                if (id != null)
                {
                    sb.Where = $"{id.ColumnName}>={row}";
                    sql      = PageSplit(sb, 0, pageSize);
                }
                else
                {
                    sql = PageSplit(sb, row, pageSize);
                }

                // 查询数据
                var dt = Session.Query(sql, null);
                if (dt == null || dt.Rows.Count == 0)
                {
                    break;
                }

                var count = dt.Rows.Count;
                WriteLog("同步[{0}/{1}]数据 {2:n0} + {3:n0}", table.Name, ConnName, row, count);

                // 进度报告
                progress?.Invoke((Int32)row, dt);

                // 消费数据
                writeDb.Tell(dt);

                // 下一页
                total += count;
                //if (count < pageSize) break;

                // 自增分割时,取最后一行
                if (id != null)
                {
                    row = dt.Get <Int64>(count - 1, id.ColumnName) + 1;
                }
                else
                {
                    row += pageSize;
                }
            }

            // 通知写入完成
            writeDb.Stop(-1);

            sw.Stop();
            var ms = sw.Elapsed.TotalMilliseconds;

            WriteLog("同步[{0}/{1}]完成,共[{2:n0}]行,耗时{3:n0}ms,速度{4:n0}tps", table.Name, ConnName, total, ms, total * 1000L / ms);

            // 返回总行数
            return(total);
        }
Exemple #14
0
 /// <summary>执行SQL查询,返回记录集</summary>
 /// <param name="builder">SQL语句</param>
 /// <param name="startRowIndex">开始行,0表示第一行</param>
 /// <param name="maximumRows">最大返回行数,0表示所有行</param>
 /// <param name="tableNames">所依赖的表的表名</param>
 /// <returns></returns>
 public DataSet Select(SelectBuilder builder, Int32 startRowIndex, Int32 maximumRows, params String[] tableNames)
 {
     builder = PageSplit(builder, startRowIndex, maximumRows);
     return(Select(builder.ToString(), tableNames));
 }
Exemple #15
0
        /// <summary>RowNumber分页算法</summary>
        /// <param name="builder">查询生成器</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <returns></returns>
        static SelectBuilder RowNumber(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
        {
            //if (maximumRows < 1)
            //    sql = String.Format("Select * From (Select *, row_number() over({2}) as rowNumber From {1}) XCode_Temp_b Where rowNumber>={0}", startRowIndex + 1, sql, orderBy);
            //else
            //    sql = String.Format("Select * From (Select *, row_number() over({3}) as rowNumber From {1}) XCode_Temp_b Where rowNumber Between {0} And {2}", startRowIndex + 1, sql, startRowIndex + maximumRows, orderBy);

            // 如果包含分组,则必须作为子查询
            SelectBuilder builder1 = builder.CloneWithGroupBy("XCode_T0");
            builder1.Column = String.Format("{0}, row_number() over(Order By {1}) as rowNumber", builder.ColumnOrDefault, builder.OrderBy ?? builder.KeyOrder);

            SelectBuilder builder2 = builder1.AsChild("XCode_T1");
            // 结果列保持原样
            builder2.Column = builder.Column;
            // row_number()直接影响了排序,这里不再需要
            builder2.OrderBy = null;
            if (maximumRows < 1)
                builder2.Where = String.Format("rowNumber>={0}", startRowIndex + 1);
            else
                builder2.Where = String.Format("rowNumber Between {0} And {1}", startRowIndex + 1, startRowIndex + maximumRows);

            return builder2;
        }
Exemple #16
0
        /// <summary>备份单表数据,抽取数据和写入文件双线程</summary>
        /// <remarks>
        /// 最大支持21亿行
        /// </remarks>
        /// <param name="table">数据表</param>
        /// <param name="stream">目标数据流</param>
        /// <returns></returns>
        public virtual Int32 Backup(IDataTable table, Stream stream)
        {
            using var span = Tracer?.NewSpan("db:Backup", table.Name);

            // 并行写入文件,提升吞吐
            var writeFile = new WriteFileActor
            {
                Stream = stream,

                // 最多同时堆积数
                BoundedCapacity = 4,
                TracerParent    = span,
                Tracer          = Tracer,
                Log             = Log,
            };

            var tableName = Dal.Db.FormatName(table);
            var sb        = new SelectBuilder {
                Table = tableName
            };
            var connName = Dal.ConnName;

            var extracer = GetExtracter(table);

            // 总行数
            writeFile.Total = Dal.SelectCount(sb);
            WriteLog("备份[{0}/{1}]开始,共[{2:n0}]行,抽取器{3}", table, connName, writeFile.Total, extracer);

            // 临时关闭日志
            var old = Dal.Db.ShowSQL;

            Dal.Db.ShowSQL      = false;
            Dal.Session.ShowSQL = false;

            var total = 0;
            var sw    = Stopwatch.StartNew();

            try
            {
                foreach (var dt in extracer.Fetch())
                {
                    var row   = extracer.Row;
                    var count = dt.Rows.Count;
                    WriteLog("备份[{0}/{1}]数据 {2:n0} + {3:n0}", table, connName, row, count);
                    if (count == 0)
                    {
                        break;
                    }

                    // 字段名更换为属性名
                    for (var i = 0; i < dt.Columns.Length; i++)
                    {
                        var dc = table.GetColumn(dt.Columns[i]);
                        if (dc != null)
                        {
                            dt.Columns[i] = dc.Name;
                        }
                    }

                    // 进度报告、消费数据
                    OnProcess(table, row, dt, writeFile);

                    total += count;
                }

                // 通知写入完成
                writeFile.Stop(-1);
            }
            catch (Exception ex)
            {
                span?.SetError(ex, table);
                throw;
            }
            finally
            {
                Dal.Db.ShowSQL      = old;
                Dal.Session.ShowSQL = old;
            }

            sw.Stop();
            var ms = sw.Elapsed.TotalMilliseconds;

            WriteLog("备份[{0}/{1}]完成,共[{2:n0}]行,耗时{3:n0}ms,速度{4:n0}tps", table, connName, total, ms, total * 1000L / ms);

            // 返回总行数
            return(total);
        }
Exemple #17
0
        /// <summary>构造分页SQL</summary>
        /// <remarks>
        /// 两个构造分页SQL的方法,区别就在于查询生成器能够构造出来更好的分页语句,尽可能的避免子查询。
        /// MS体系的分页精髓就在于唯一键,当唯一键带有Asc/Desc/Unkown等排序结尾时,就采用最大最小值分页,否则使用较次的TopNotIn分页。
        /// TopNotIn分页和MaxMin分页的弊端就在于无法完美的支持GroupBy查询分页,只能查到第一页,往后分页就不行了,因为没有主键。
        /// </remarks>
        /// <param name="builder">查询生成器</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <returns>分页SQL</returns>
        public override SelectBuilder PageSplit(SelectBuilder builder, Int32 startRowIndex, Int32 maximumRows)
        {
            // 从第一行开始,不需要分页
            if (startRowIndex <= 0)
            {
                if (maximumRows > 0) builder.OrderBy += String.Format(" limit {0}", maximumRows);
                return builder;
            }
            if (maximumRows < 1) throw new NotSupportedException("不支持取第几条数据之后的所有数据!");

            builder.OrderBy += String.Format(" limit {0} offset {1}", startRowIndex, maximumRows);
            return builder;
        }
Exemple #18
0
        /// <summary>备份单表数据</summary>
        /// <remarks>
        /// 最大支持21亿行
        /// </remarks>
        /// <param name="table">数据表</param>
        /// <param name="stream">目标数据流</param>
        /// <param name="progress">进度回调,参数为已处理行数和当前页表</param>
        /// <returns></returns>
        public Int32 Backup(IDataTable table, Stream stream, Action <Int32, DbTable> progress = null)
        {
            var writeFile = new WriteFileActor
            {
                Stream = stream,

                // 最多同时堆积数
                BoundedCapacity = 4,
            };
            //writeFile.Start();

            // 自增
            var id = table.Columns.FirstOrDefault(e => e.Identity);

            // 主键
            if (id == null)
            {
                var pks = table.PrimaryKeys;
                if (pks != null && pks.Length == 1 && pks[0].DataType.IsInt())
                {
                    id = pks[0];
                }
            }

            var sb = new SelectBuilder
            {
                Table = Db.FormatTableName(table.TableName)
            };

            // 总行数
            writeFile.Total = SelectCount(sb);
            WriteLog("备份[{0}/{1}]开始,共[{2:n0}]行", table, ConnName, writeFile.Total);

            var row      = 0L;
            var pageSize = 10_000;
            var total    = 0;
            var sw       = Stopwatch.StartNew();

            while (total < writeFile.Total)
            {
                var sql = "";
                // 分割数据页,自增或分页
                if (id != null)
                {
                    sb.Where = $"{id.ColumnName}>={row}";
                    sql      = PageSplit(sb, 0, pageSize);
                }
                else
                {
                    sql = PageSplit(sb, row, pageSize);
                }

                // 查询数据
                var dt = Session.Query(sql, null);
                if (dt == null)
                {
                    break;
                }

                var count = dt.Rows.Count;
                WriteLog("备份[{0}/{1}]数据 {2:n0} + {3:n0}", table, ConnName, row, count);
                if (count == 0)
                {
                    break;
                }

                // 进度报告
                progress?.Invoke((Int32)row, dt);

                // 消费数据
                writeFile.Tell(dt);

                // 下一页
                total += count;
                //if (count < pageSize) break;

                // 自增分割时,取最后一行
                if (id != null)
                {
                    row = dt.Get <Int64>(count - 1, id.ColumnName) + 1;
                }
                else
                {
                    row += pageSize;
                }
            }

            // 通知写入完成
            writeFile.Stop(-1);

            sw.Stop();
            var ms = sw.Elapsed.TotalMilliseconds;

            WriteLog("备份[{0}/{1}]完成,共[{2:n0}]行,耗时{3:n0}ms,速度{4:n0}tps", table, ConnName, total, ms, total * 1000L / ms);

            // 返回总行数
            return(total);
        }
Exemple #19
0
        /// <summary>备份单表数据</summary>
        /// <remarks>
        /// 最大支持21亿行
        /// </remarks>
        /// <param name="table">数据表</param>
        /// <param name="stream">目标数据流</param>
        /// <param name="progress">进度回调,参数为已处理行数和当前页表</param>
        /// <returns></returns>
        public Int32 Backup(IDataTable table, Stream stream, Action <Int64, DbTable> progress = null)
        {
            var writeFile = new WriteFileActor
            {
                Stream = stream,

                // 最多同时堆积数
                BoundedCapacity = 4,
            };

            // 自增
            var id = table.Columns.FirstOrDefault(e => e.Identity);

            if (id == null)
            {
                var pks = table.PrimaryKeys;
                if (pks != null && pks.Length == 1 && pks[0].DataType.IsInt())
                {
                    id = pks[0];
                }
            }
            var tableName = Db.FormatName(table);
            var sb        = new SelectBuilder {
                Table = tableName
            };

            // 总行数
            writeFile.Total = SelectCount(sb);
            WriteLog("备份[{0}/{1}]开始,共[{2:n0}]行", table, ConnName, writeFile.Total);

            IExtracter <DbTable> extracer = new PagingExtracter(this, tableName);

            if (id != null)
            {
                extracer = new IdExtracter(this, tableName, id.ColumnName);
            }

            var sw    = Stopwatch.StartNew();
            var total = 0;

            foreach (var dt in extracer.Fetch())
            {
                var count = dt.Rows.Count;
                WriteLog("备份[{0}/{1}]数据 {2:n0} + {3:n0}", table, ConnName, extracer.Row, count);
                if (count == 0)
                {
                    break;
                }

                // 进度报告
                progress?.Invoke(extracer.Row, dt);

                // 消费数据
                writeFile.Tell(dt);

                total += count;
            }

            // 通知写入完成
            writeFile.Stop(-1);

            sw.Stop();
            var ms = sw.Elapsed.TotalMilliseconds;

            WriteLog("备份[{0}/{1}]完成,共[{2:n0}]行,耗时{3:n0}ms,速度{4:n0}tps", table, ConnName, total, ms, total * 1000L / ms);

            // 返回总行数
            return(total);
        }
Exemple #20
0
 /// <summary>执行SQL查询,返回总记录数</summary>
 /// <param name="sb">查询生成器</param>
 /// <returns></returns>
 public Task <Int64> SelectCountAsync(SelectBuilder sb)
 {
     return(QueryByCacheAsync(sb, "", "", (s, k2, k3) => Session.QueryCountAsync(s), nameof(SelectCountAsync)));
 }
Exemple #21
0
        /// <summary>双Top分页,因为没有使用not in,性能比NotIn要好。语句必须有排序,不需额外指定排序列</summary>
        /// <param name="builder"></param>
        /// <param name="startRowIndex"></param>
        /// <param name="maximumRows"></param>
        /// <param name="queryCountCallback">查询总记录数的委托,近供DoubleTop使用</param>
        /// <returns></returns>
        static SelectBuilder DoubleTop(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows, Func <SelectBuilder, Int64> queryCountCallback)
        {
            if (builder.Keys == null)
            {
                throw new ArgumentNullException("Key", "DoubleTop分页算法要求指定排序列!" + builder.ToString());
            }

            // 采用DoubleTop分页,最后一页可能有问题,需要特殊处理
            if (queryCountCallback != null)
            {
                // 查询总记录数,计算是否最后一页
                var count = queryCountCallback(builder);
                // 刚好相等的就不必处理了
                if (startRowIndex + maximumRows > count)
                {
                    maximumRows = count - startRowIndex;
                }
            }

            // 分页标准 Select (20,10,ID Desc)
            // 1,按原始排序取20+10行,此时目标页位于底部
            // 2,倒序过来取10行,得到目标页,但是顺序是反的
            // 3,再倒序一次
            // 显然,原始语句必须有排序,否则无法倒序。另外,也不能处理maximumRows<1的情况
            // Select * From (Select Top 10 * From (Select Top 20+10 * From Table Order By ID Desc) Order By ID Asc) Order By ID Desc

            // 找到排序,优先采用排序字句来做双Top排序
            String orderby = builder.OrderBy ?? builder.KeyOrder;

            Boolean[] isdescs = null;
            String[]  keys    = SelectBuilder.Split(orderby, out isdescs);

            // 把排序反过来
            Boolean[] isdescs2 = new Boolean[keys.Length];
            for (int i = 0; i < keys.Length; i++)
            {
                if (isdescs != null && isdescs.Length > i)
                {
                    isdescs2[i] = !isdescs[i];
                }
                else
                {
                    isdescs2[i] = true;
                }
            }
            String reversekeyorder = SelectBuilder.Join(keys, isdescs2);

            // 构建Select Top 20 * From Table Order By ID Asc
            SelectBuilder builder1 = builder.Clone().AppendColumn(keys).Top(startRowIndex + maximumRows);

            // 必须加一个排序,否则会被优化掉而导致出错
            if (String.IsNullOrEmpty(builder1.OrderBy))
            {
                builder1.OrderBy = builder1.KeyOrder;
            }

            SelectBuilder builder2 = builder1.AsChild("XCode_T0").Top(maximumRows);

            // 要反向排序
            builder2.OrderBy = reversekeyorder;

            SelectBuilder builder3 = builder2.AsChild("XCode_T1");

            // 结果列保持原样
            builder3.Column = builder.Column;
            // 让结果正向排序
            builder3.OrderBy = orderby;

            return(builder3);
        }
Exemple #22
0
        /// <summary>备份单表数据</summary>
        /// <remarks>
        /// 最大支持21亿行
        /// </remarks>
        /// <param name="table">数据表</param>
        /// <param name="stream">目标数据流</param>
        /// <param name="progress">进度回调,参数为已处理行数和当前页表</param>
        /// <returns></returns>
        public Int32 Backup(String table, Stream stream, Action <Int32, DbTable> progress = null)
        {
            var writeFile = new WriteFileActor
            {
                Stream = stream,

                // 最多同时堆积数页
                BoundedCapacity = 4,
            };
            //writeFile.Start();

            var sb = new SelectBuilder
            {
                Table = Db.FormatTableName(table)
            };

            var row      = 0;
            var pageSize = 10_000;
            var total    = 0;
            var sw       = Stopwatch.StartNew();

            while (true)
            {
                // 分页
                var sb2 = PageSplit(sb, row, pageSize);

                // 查询数据
                var dt = Session.Query(sb2.ToString(), null);
                if (dt == null)
                {
                    break;
                }

                var count = dt.Rows.Count;
                WriteLog("备份[{0}/{1}]数据 {2:n0} + {3:n0}", table, ConnName, row, count);

                // 进度报告
                progress?.Invoke(row, dt);

                // 消费数据
                writeFile.Tell(dt);

                // 下一页
                total += count;
                if (count < pageSize)
                {
                    break;
                }
                row += pageSize;
            }

            // 通知写入完成
            writeFile.Stop(-1);

            sw.Stop();
            var ms = sw.Elapsed.TotalMilliseconds;

            WriteLog("备份[{0}/{1}]完成,共[{2:n0}]行,耗时{3:n0}ms,速度{4:n0}tps", table, ConnName, total, ms, total * 1000L / ms);

            // 返回总行数
            return(total);
        }
Exemple #23
0
 /// <summary>执行SQL查询,返回总记录数</summary>
 /// <param name="builder">查询生成器</param>
 /// <returns>总记录数</returns>
 public virtual Int64 QueryCount(SelectBuilder builder) => ExecuteScalar <Int64>(builder.SelectCount().ToString(), CommandType.Text, builder.Parameters.ToArray());
Exemple #24
0
        /// <summary>同步单表数据</summary>
        /// <remarks>
        /// 把数据同一张表同步到另一个库
        /// </remarks>
        /// <param name="table">数据表</param>
        /// <param name="connName">目标连接名</param>
        /// <param name="syncSchema">同步架构</param>
        /// <param name="progress">进度回调,参数为已处理行数和当前页表</param>
        /// <returns></returns>
        public Int32 Sync(IDataTable table, String connName, Boolean syncSchema = true, Action <Int32, DbTable> progress = null)
        {
            var dal = connName.IsNullOrEmpty() ? null : Create(connName);

            var writeDb = new WriteDbActor
            {
                Table = table,
                Dal   = dal,

                // 最多同时堆积数页
                BoundedCapacity = 4,
            };

            var sw = Stopwatch.StartNew();

            // 表结构
            if (syncSchema)
            {
                dal.SetTables(table);
            }

            var sb = new SelectBuilder
            {
                Table = Db.FormatTableName(table.TableName)
            };

            var row      = 0;
            var pageSize = 10_000;
            var total    = 0;

            while (true)
            {
                // 分页
                var sb2 = PageSplit(sb, row, pageSize);

                // 查询数据
                var dt = Session.Query(sb2.ToString(), null);
                if (dt == null)
                {
                    break;
                }

                var count = dt.Rows.Count;
                WriteLog("同步[{0}/{1}]数据 {2:n0} + {3:n0}", table.Name, ConnName, row, count);

                // 进度报告
                progress?.Invoke(row, dt);

                // 消费数据
                writeDb.Tell(dt);

                // 下一页
                total += count;
                if (count < pageSize)
                {
                    break;
                }
                row += pageSize;
            }

            // 通知写入完成
            writeDb.Stop(-1);

            sw.Stop();
            var ms = sw.Elapsed.TotalMilliseconds;

            WriteLog("同步[{0}/{1}]完成,共[{2:n0}]行,耗时{3:n0}ms,速度{4:n0}tps", table.Name, ConnName, total, ms, total * 1000L / ms);

            // 返回总行数
            return(total);
        }
Exemple #25
0
        /// <summary>双Top分页,因为没有使用not in,性能比NotIn要好。语句必须有排序,不需额外指定排序列</summary>
        /// <param name="builder"></param>
        /// <param name="startRowIndex"></param>
        /// <param name="maximumRows"></param>
        /// <param name="queryCountCallback">查询总记录数的委托,近供DoubleTop使用</param>
        /// <returns></returns>
        static SelectBuilder DoubleTop(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows, Func<SelectBuilder, Int64> queryCountCallback)
        {
            if (builder.Keys == null) throw new ArgumentNullException("Key", "DoubleTop分页算法要求指定排序列!" + builder.ToString());

            // 采用DoubleTop分页,最后一页可能有问题,需要特殊处理
            if (queryCountCallback != null)
            {
                // 查询总记录数,计算是否最后一页
                var count = queryCountCallback(builder);
                // 数据不足
                if (count <= startRowIndex) return null;
                // 刚好相等的就不必处理了
                if (startRowIndex + maximumRows > count) maximumRows = count - startRowIndex;
            }

            // 分页标准 Select (20,10,ID Desc)
            // 1,按原始排序取20+10行,此时目标页位于底部
            // 2,倒序过来取10行,得到目标页,但是顺序是反的
            // 3,再倒序一次
            // 显然,原始语句必须有排序,否则无法倒序。另外,也不能处理maximumRows<1的情况
            // Select * From (Select Top 10 * From (Select Top 20+10 * From Table Order By ID Desc) Order By ID Asc) Order By ID Desc

            // 找到排序,优先采用排序字句来做双Top排序
            var orderby = builder.OrderBy ?? builder.KeyOrder;
            Boolean[] isdescs = null;
            var keys = SelectBuilder.Split(orderby, out isdescs);

            // 把排序反过来
            var isdescs2 = new Boolean[keys.Length];
            for (int i = 0; i < keys.Length; i++)
            {
                if (isdescs != null && isdescs.Length > i)
                    isdescs2[i] = !isdescs[i];
                else
                    isdescs2[i] = true;
            }
            var reversekeyorder = SelectBuilder.Join(keys, isdescs2);

            // 构建Select Top 20 * From Table Order By ID Asc
            var builder1 = builder.Clone().AppendColumn(keys).Top(startRowIndex + maximumRows);
            // 必须加一个排序,否则会被优化掉而导致出错
            if (String.IsNullOrEmpty(builder1.OrderBy)) builder1.OrderBy = builder1.KeyOrder;

            var builder2 = builder1.AsChild("XCode_T0").Top(maximumRows);
            // 要反向排序
            builder2.OrderBy = reversekeyorder;

            var builder3 = builder2.AsChild("XCode_T1");
            // 结果列处理
            builder3.Column = builder.Column;
            // 如果结果列包含有“.”,即有形如tab1.id、tab2.name之类的列时设为获取子查询的全部列
            if ((!string.IsNullOrEmpty(builder3.Column)) && builder3.Column.Contains("."))
            {
                builder3.Column = "*";
            }
            // 让结果正向排序
            builder3.OrderBy = orderby;

            return builder3;
        }
Exemple #26
0
 /// <summary>根据条件把普通查询SQL格式化为分页SQL。</summary>
 /// <param name="builder">查询生成器</param>
 /// <param name="startRowIndex">开始行,0表示第一行</param>
 /// <param name="maximumRows">最大返回行数,0表示所有行</param>
 /// <returns>分页SQL</returns>
 public SelectBuilder PageSplit(SelectBuilder builder, Int32 startRowIndex, Int32 maximumRows)
 {
     //2016年7月2日 HUIYUE 取消分页SQL缓存,此部分缓存提升性能不多,但有可能会造成分页数据不准确,感觉得不偿失
     return(Db.PageSplit(builder, startRowIndex, maximumRows));
 }
Exemple #27
0
        /// <summary>RowNumber分页算法</summary>
        /// <param name="builder">查询生成器</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <returns></returns>
        static SelectBuilder RowNumber(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
        {
            //if (maximumRows < 1)
            //    sql = String.Format("Select * From (Select *, row_number() over({2}) as rowNumber From {1}) XCode_Temp_b Where rowNumber>={0}", startRowIndex + 1, sql, orderBy);
            //else
            //    sql = String.Format("Select * From (Select *, row_number() over({3}) as rowNumber From {1}) XCode_Temp_b Where rowNumber Between {0} And {2}", startRowIndex + 1, sql, startRowIndex + maximumRows, orderBy);

            // 如果包含分组,则必须作为子查询
            var builder1 = builder.CloneWithGroupBy("XCode_T0");
            //builder1.Column = String.Format("{0}, row_number() over(Order By {1}) as rowNumber", builder.ColumnOrDefault, builder.OrderBy ?? builder.KeyOrder);
            // 不必追求极致,把所有列放出来
            builder1.Column = "*, row_number() over(Order By {0}) as rowNumber".F(builder.OrderBy ?? builder.KeyOrder);

            var builder2 = builder1.AsChild("XCode_T1");
            // 结果列处理
            //builder2.Column = builder.Column;
            //// 如果结果列包含有“.”,即有形如tab1.id、tab2.name之类的列时设为获取子查询的全部列
            //if ((!string.IsNullOrEmpty(builder2.Column)) && builder2.Column.Contains("."))
            //{
            //    builder2.Column = "*";
            //}
            // 不必追求极致,把所有列放出来
            builder2.Column = "*";

            // row_number()直接影响了排序,这里不再需要
            builder2.OrderBy = null;
            if (maximumRows < 1)
                builder2.Where = String.Format("rowNumber>={0}", startRowIndex + 1);
            else
                builder2.Where = String.Format("rowNumber Between {0} And {1}", startRowIndex + 1, startRowIndex + maximumRows);

            return builder2;
        }
Exemple #28
0
        /// <summary>构造分页SQL</summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <param name="keyColumn">唯一键。用于not in分页</param>
        /// <returns>分页SQL</returns>
        public override String PageSplit(String sql, Int64 startRowIndex, Int64 maximumRows, String keyColumn)
        {
            // 从第一行开始,不需要分页
            if (startRowIndex <= 0 && maximumRows < 1)
            {
                return(sql);
            }

            if (startRowIndex > 0)
            {
                // 指定了起始行,并且是SQL2005及以上版本,使用MS SQL 2012特有的分页算法
                if (IsSQL2012)
                {
                    // 从第一行开始,不需要分页
                    if (startRowIndex <= 0)
                    {
                        if (maximumRows < 1)
                        {
                            return(sql);
                        }

                        var sql_ = FormatSqlserver2012SQL(sql);
                        return($"{sql_} offset 1 rows fetch next {maximumRows} rows only ");
                    }
                    if (maximumRows < 1)
                    {
                        throw new NotSupportedException("不支持取第几条数据之后的所有数据!");
                    }

                    var sql__ = FormatSqlserver2012SQL(sql);
                    return($"{sql__} offset {startRowIndex} rows fetch next {maximumRows} rows only ");
                }

                // 指定了起始行,并且是SQL2005及以上版本,使用RowNumber算法
                //if (IsSQL2005)
                {
                    //return PageSplitRowNumber(sql, startRowIndex, maximumRows, keyColumn);
                    var builder = new SelectBuilder();
                    builder.Parse(sql);
                    //return MSPageSplit.PageSplit(builder, startRowIndex, maximumRows, IsSQL2005).ToString();

                    return(PageSplit(builder, startRowIndex, maximumRows).ToString());
                }
            }

            // 如果没有Order By,直接调用基类方法
            // 先用字符串判断,命中率高,这样可以提高处理效率
            if (!sql.Contains(" Order "))
            {
                if (!sql.ToLower().Contains(" order "))
                {
                    return(base.PageSplit(sql, startRowIndex, maximumRows, keyColumn));
                }
            }
            //// 使用正则进行严格判断。必须包含Order By,并且它右边没有右括号),表明有order by,且不是子查询的,才需要特殊处理
            //MatchCollection ms = Regex.Matches(sql, @"\border\s*by\b([^)]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            //if (ms == null || ms.Count < 1 || ms[0].Index < 1)
            var sql2    = sql;
            var orderBy = CheckOrderClause(ref sql2);

            if (String.IsNullOrEmpty(orderBy))
            {
                return(base.PageSplit(sql, startRowIndex, maximumRows, keyColumn));
            }
            // 已确定该sql最外层含有order by,再检查最外层是否有top。因为没有top的order by是不允许作为子查询的
            if (Regex.IsMatch(sql, @"^[^(]+\btop\b", RegexOptions.Compiled | RegexOptions.IgnoreCase))
            {
                return(base.PageSplit(sql, startRowIndex, maximumRows, keyColumn));
            }
            //String orderBy = sql.Substring(ms[0].Index);

            // 从第一行开始,不需要分页
            if (startRowIndex <= 0)
            {
                if (maximumRows < 1)
                {
                    return(sql);
                }
                else
                {
                    return(String.Format("Select Top {0} * From {1} {2}", maximumRows, CheckSimpleSQL(sql2), orderBy));
                }
                //return String.Format("Select Top {0} * From {1} {2}", maximumRows, CheckSimpleSQL(sql.Substring(0, ms[0].Index)), orderBy);
            }

            #region Max/Min分页
            // 如果要使用max/min分页法,首先keyColumn必须有asc或者desc
            var kc = keyColumn.ToLower();
            if (kc.EndsWith(" desc") || kc.EndsWith(" asc") || kc.EndsWith(" unknown"))
            {
                var str = PageSplitMaxMin(sql, startRowIndex, maximumRows, keyColumn);
                if (!String.IsNullOrEmpty(str))
                {
                    return(str);
                }
                keyColumn = keyColumn.Substring(0, keyColumn.IndexOf(" "));
            }
            #endregion

            sql = CheckSimpleSQL(sql2);

            if (String.IsNullOrEmpty(keyColumn))
            {
                throw new ArgumentNullException("keyColumn", "分页要求指定主键列或者排序字段!");
            }

            if (maximumRows < 1)
            {
                sql = String.Format("Select * From {1} Where {2} Not In(Select Top {0} {2} From {1} {3}) {3}", startRowIndex, sql, keyColumn, orderBy);
            }
            else
            {
                sql = String.Format("Select Top {0} * From {1} Where {2} Not In(Select Top {3} {2} From {1} {4}) {4}", maximumRows, sql, keyColumn, startRowIndex, orderBy);
            }
            return(sql);
        }
Exemple #29
0
 public override SelectBuilder PageSplit(SelectBuilder builder, int startRowIndex, int maximumRows)
 {
     return(MSPageSplit.PageSplit(builder, startRowIndex, maximumRows, false, b => CreateSession().QueryCount(b)));
 }
Exemple #30
0
 /// <summary>执行SQL查询,返回记录集</summary>
 /// <param name="builder">SQL语句</param>
 /// <param name="startRowIndex">开始行,0表示第一行</param>
 /// <param name="maximumRows">最大返回行数,0表示所有行</param>
 /// <param name="tableNames">所依赖的表的表名</param>
 /// <returns></returns>
 public DataSet Select(SelectBuilder builder, Int32 startRowIndex, Int32 maximumRows, params String[] tableNames)
 {
     builder = PageSplit(builder, startRowIndex, maximumRows);
     return Select(builder.ToString(), tableNames);
 }
 /// <summary>执行SQL查询,返回总记录数</summary>
 /// <param name="sb">查询生成器</param>
 /// <returns></returns>
 public Int32 SelectCount(SelectBuilder sb)
 {
     return((Int32)QueryByCache(sb, "", "", (s, k2, k3) => Session.QueryCount(s), nameof(SelectCount)));
 }
Exemple #32
0
        /// <summary>执行SQL查询,返回总记录数</summary>
        /// <param name="sb">查询生成器</param>
        /// <param name="tableNames">所依赖的表的表名</param>
        /// <returns></returns>
        public Int32 SelectCount(SelectBuilder sb, params String[] tableNames)
        {
            String sql = sb.ToString();
            String cacheKey = sql + "_SelectCount" + "_" + ConnName;
            Int32 rs = 0;
            if (EnableCache && XCache.TryGetItem(cacheKey, out rs)) return rs;

            Interlocked.Increment(ref _QueryTimes);
            rs = (Int32)Session.QueryCount(sb);

            if (EnableCache) XCache.Add(cacheKey, rs, tableNames);

            return rs;
        }
Exemple #33
0
        /// <summary>构造分页SQL</summary>
        /// <remarks>
        /// 两个构造分页SQL的方法,区别就在于查询生成器能够构造出来更好的分页语句,尽可能的避免子查询。
        /// MS体系的分页精髓就在于唯一键,当唯一键带有Asc/Desc/Unkown等排序结尾时,就采用最大最小值分页,否则使用较次的TopNotIn分页。
        /// TopNotIn分页和MaxMin分页的弊端就在于无法完美的支持GroupBy查询分页,只能查到第一页,往后分页就不行了,因为没有主键。
        /// </remarks>
        /// <param name="builder">查询生成器</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <returns>分页SQL</returns>
        public virtual SelectBuilder PageSplit(SelectBuilder builder, Int32 startRowIndex, Int32 maximumRows)
        {
            // 从第一行开始,不需要分页
            if (startRowIndex <= 0 && maximumRows < 1) return builder;

            var sql = PageSplit(builder.ToString(), startRowIndex, maximumRows, builder.Key);
            var sb = new SelectBuilder();
            sb.Parse(sql);
            return sb;
        }
Exemple #34
0
        /// <summary>根据条件把普通查询SQL格式化为分页SQL。</summary>
        /// <remarks>
        /// 因为需要继承重写的原因,在数据类中并不方便缓存分页SQL。
        /// 所以在这里做缓存。
        /// </remarks>
        /// <param name="builder">查询生成器</param>
        /// <param name="startRowIndex">开始行,0表示第一行</param>
        /// <param name="maximumRows">最大返回行数,0表示所有行</param>
        /// <returns>分页SQL</returns>
        public SelectBuilder PageSplit(SelectBuilder builder, Int32 startRowIndex, Int32 maximumRows)
        {
            String cacheKey = String.Format("{0}_{1}_{2}_{3}_", builder, startRowIndex, maximumRows, ConnName);

            return _PageSplitCache2.GetItem<SelectBuilder, Int32, Int32>(cacheKey, builder, startRowIndex, maximumRows, (k, b, s, m) =>
            {
                return Db.PageSplit(b, s, m);
            });
        }
Exemple #35
0
        /// <summary>作为子查询</summary>
        /// <param name="alias">别名,某些数据库可能需要使用as</param>
        /// <returns></returns>
        public SelectBuilder AsChild(String alias = null)
        {
            var t = this;
            // 如果包含排序,则必须有Top,否则去掉
            var hasOrderWithoutTop = !String.IsNullOrEmpty(t.OrderBy) && !ColumnOrDefault.StartsWithIgnoreCase("top ");
            if (hasOrderWithoutTop)
            {
                t = this.Clone();
                t.OrderBy = null;
            }

            var builder = new SelectBuilder();
            if (String.IsNullOrEmpty(alias))
                builder.Table = String.Format("({0})", t.ToString());
            else
                builder.Table = String.Format("({0}) {1}", t.ToString(), alias);

            // 把排序加载外层
            if (hasOrderWithoutTop) builder.OrderBy = this.OrderBy;

            return builder;
        }
Exemple #36
0
        /// <summary>双Top分页,因为没有使用not in,性能比NotIn要好。语句必须有排序,不需额外指定排序列</summary>
        /// <param name="builder"></param>
        /// <param name="startRowIndex"></param>
        /// <param name="maximumRows"></param>
        /// <param name="queryCountCallback">查询总记录数的委托,近供DoubleTop使用</param>
        /// <returns></returns>
        static SelectBuilder DoubleTop(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows, Func <SelectBuilder, Int64> queryCountCallback)
        {
            if (builder.Keys == null)
            {
                throw new ArgumentNullException("Key", "DoubleTop分页算法要求指定排序列!" + builder.ToString());
            }

            // 采用DoubleTop分页,最后一页可能有问题,需要特殊处理
            if (queryCountCallback != null)
            {
                // 查询总记录数,计算是否最后一页
                var count = queryCountCallback(builder);
                // 数据不足
                if (count <= startRowIndex)
                {
                    return(null);
                }
                // 刚好相等的就不必处理了
                if (startRowIndex + maximumRows > count)
                {
                    maximumRows = count - startRowIndex;
                }
            }

            // 分页标准 Select (20,10,ID Desc)
            // 1,按原始排序取20+10行,此时目标页位于底部
            // 2,倒序过来取10行,得到目标页,但是顺序是反的
            // 3,再倒序一次
            // 显然,原始语句必须有排序,否则无法倒序。另外,也不能处理maximumRows<1的情况
            // Select * From (Select Top 10 * From (Select Top 20+10 * From Table Order By ID Desc) Order By ID Asc) Order By ID Desc

            // 找到排序,优先采用排序字句来做双Top排序
            var orderby = builder.OrderBy ?? builder.KeyOrder;
            var keys    = SelectBuilder.Split(orderby, out var isdescs);

            // 把排序反过来
            var isdescs2 = new Boolean[keys.Length];

            for (Int32 i = 0; i < keys.Length; i++)
            {
                if (isdescs != null && isdescs.Length > i)
                {
                    isdescs2[i] = !isdescs[i];
                }
                else
                {
                    isdescs2[i] = true;
                }
            }
            var reversekeyorder = SelectBuilder.Join(keys, isdescs2);

            // 构建Select Top 20 * From Table Order By ID Asc
            var builder1 = builder.Clone().AppendColumn(keys).Top(startRowIndex + maximumRows);

            // 必须加一个排序,否则会被优化掉而导致出错
            if (String.IsNullOrEmpty(builder1.OrderBy))
            {
                builder1.OrderBy = builder1.KeyOrder;
            }

            var builder2 = builder1.AsChild("XCode_T0", true).Top(maximumRows);

            // 要反向排序
            builder2.OrderBy = reversekeyorder;

            var builder3 = builder2.AsChild("XCode_T1", true);

            // 结果列处理
            builder3.Column = builder.Column;
            // 如果结果列包含有“.”,即有形如tab1.id、tab2.name之类的列时设为获取子查询的全部列
            if ((!string.IsNullOrEmpty(builder3.Column)) && builder3.Column.Contains("."))
            {
                builder3.Column = "*";
            }
            // 让结果正向排序
            builder3.OrderBy = orderby;

            return(builder3);
        }