【问题标题】:How to Select Records from large table batch wise in SQL如何在 SQL 中批量从大表中选择记录
【发布时间】:2020-09-28 23:35:10
【问题描述】:

我有一个包含数十亿条记录的表。我想使用 c#while 循环批量选择记录。我使用了OffsetFetch Next,但是需要很长时间才能得到结果。如果表包含低于 100k 的记录,它工作正常。批量选择记录的最佳方法是什么?

示例代码

int Count  = 500000 // Select Count(id) from table
int OffSet = 1; //Next Time 100000
int FetchNext = 100000; //Next Time 200000

 while (OffSet < Count)
{
   _strsqlcommand = "Select id from table ORDER BY id  OFFSET " + OffSet + " ROWS  FETCH NEXT " + FetchNext + " ROWS ONLY"

_sqlCommand = new SqlCommand(_strsqlcommand, _sqlConnection);
_sqlDataReader = _sqlCommand.ExecuteReader();


 OffSet += FetchNext;
}

【问题讨论】:

  • 这是正确的做法。性能问题需要在sql server端解决。例如,id 是否已编入索引?另外...您确定需要提取所有数十亿行吗?
  • @JNevill 是的,我必须分批提取数十亿行。我实际上并不了解从 1 到 100000 的速度,但第二批 1,00,000 到 2,00,000 的速度非常慢。我担心从 50,00,000 到 60,00,000 需要多少时间。
  • 处理大文件的最佳方法是使用 SQL Server 命令行实用程序,如 SQLCMD.exe(请参阅 docs.microsoft.com/en-us/sql/tools/…)。您可以创建一个 CSV 文件并将 CSV 导入 c#。这些实用程序旨在存档大型数据库并且运行速度比运行 C# 代码快得多。这些实用程序也内置在 Power Shell 中,因此您可以运行 power shell 来创建 csv。相比之下,我看到在 c# 中运行需要超过 1/2 小时,而在几分钟内运行 sqlcmd.exe。
  • 在,但说真的,每次你想拉一个页面时,需要多长时间才能按 id 百万+行排序?按 id 排序意味着它是一个整数;如果你想要一大堆行,为什么不直接在 x 和 y 之间选择呢?他们必须井井有条吗?为什么不从表中选择 *?为什么每次都增加 fetch size?
  • @CaiusJard 不能使用 select * from the table.I didn't use it between.到目前为止,很多人说 fetch 比 between 更快。是的,我增加了获取大小,因为我想分批选择。

标签: c# sql offset next


【解决方案1】:

如果您的 Id 是数字并且您的网络带宽足够好:

  1. 首先,找出Id的最大值和最小值。
  2. 其次,创建要在 where 子句中使用的数字范围(对许多记录进行排序是一项高成本操作。使用 Where Id &gt; @A and Id&lt;@Bfetch next 快得多)。
  3. 使用 TPL 从数据库中提取数据。
namespace BatchLoad
{
    class Program
    {
        static void Main(string[] args)
        {

            var start = DateTime.Now;

            int MinId = 169328112;
            int MaxId = 505690099;

            int BatchCount = 1000000;
            List<QueryExe> Alllist=new List<QueryExe>(BatchCount);
            var stack =new  ConcurrentStack<int>();

            int i = MinId;
            int index = 0;
            while(i<MaxId+1)
            {
                int minid = i;
                int maxid = i + BatchCount;
                string q = $"SELECT  [Id]   FROM YourTable with(nolock)  WHERE Id>={minid} and Id<{maxid} ";
                string c =    "Data Source=.;Initial Catalog=YourDatabase;Persist Security Info=True;User ID=??;Password=!!!;MultipleActiveResultSets=True";
                i = maxid;
                Alllist.Add(new QueryExe(q,c, index));
                index++;
            }
           
            long ProccessCount = 0;
            Parallel.ForEach(Alllist, new ParallelOptions {MaxDegreeOfParallelism = 100}, command =>
            {
                
                Task.Yield();
                var temp = command.GetId();
                if(temp?.Count>0)stack.PushRange(temp.ToArray());
                Interlocked.Add(ref ProccessCount,1);
                var donitems = Interlocked.Read(ref ProccessCount);
                if (donitems %10 == 0)
                {
                    Console.WriteLine($"{donitems} / {Alllist.Count}  TotalM={stack.Count/1000000} Total={stack.Count}");
                 
                }
               
            });
            GC.Collect();
            Console.WriteLine($"Done>{DateTime.Now.Subtract(start).TotalMilliseconds} count ={stack.Count/1000000}");
            Console.ReadLine();

        }

    }

    public class QueryExe
    {
        private string Q = "";
        private string C = "";
        private int i = 0;
        public QueryExecutor(string Q, string C, int i)
        {
            this.Q = Q;
            this.C = C;
            this.i = i;
        }

        public List<int> GetId()
        {
            var result = new List<int>();

            try
            {
                SqlConnection conn = new SqlConnection(C);
                SqlCommand command = new SqlCommand(this.Q, conn);
                command.CommandTimeout = 180;
                using (conn)
                {
                    conn.Open();
                    using (SqlDataReader reader = command.ExecuteReader())
                    {

                        while (reader.Read())
                        {
                            result.Add(reader.GetInt32(0));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception>{i}");
            }

            return result;
        }

    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多