الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

تنفيذ جملة Insert يحتاج لوقت طويل جداً

بدأه الفريد في 10 أغسطس 2010 · 8 رد · 914 مشاهدة · في ADO.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم و رحمة الله

الإخوة الكرام واجهت مشكلة في SQLite و SqlCe

عند استخدم جملة الإدخال insert إلى قاعدة البيانات تعمل بشكل سريع جداً إذا كان عدد السجلات 25 أو 30 ألف سجل ... أما اذا كان أكثر من ذلك فهو يتباطأ بشكل رهيب .

قمت بالتجربة على نوعين من قواعد البيانات SQLite و SqlCe و نفس المشكلة . إلى درجة تقسيم السجلات إلى مجموعات و تنفيذها . و نفس الوضع ... و عمل thread لكل مجموعة و نفس الوضع بط شديد .

ألاحظ مثلاً في برنامج SQLite Expert يقوم بإدخال السجلات بسرعة عالية جداً . و الاستنتاج أن الخلل في طريقة عملي ..

و أريد منكم المساعدة .. و جزاكم الله خيراً

توليد قائمة بسجلات افتراضية

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim LM As String = Nothing
        Dim N1 As Long = 0
        Dim N2 As Long = 0
        For i = 1 To 150000
            N1 = FormatNumber(Rnd() * 10000, 0)
            N2 = FormatNumber(Rnd() * 10000, 0)
            If Len(N1 & N2) = 8 Then
                LM &= "emp" & N1 & N2
                NumberSortedList.Add(LM, "User" & i)
            End If
            LM = Nothing
        Next
        Dim th As New Threading.Thread(AddressOf Inser0011)
        th.Start(NumberSortedList)
        Debug.Print(NumberSortedList.Count)
        Debug.Print("Start")
    End Sub

و هنا الكود المستخدم في عملية الإدخال

Private Sub Inser0011(ByVal SL As SortedList)
        If conn.State = ConnectionState.Closed Then conn.Open()
        Dim dtStart, dtStart2 As DateTime
        Dim d As Double
        Dim n, n0 As Long
        Dim insertString As String = Nothing
        Dim iCount As Long = SL.Count
        dtStart = DateTime.Now
        For n0 = 0 To SL.Count - 1 Step 5000
            Using mytransaction As SQLiteTransaction = conn.BeginTransaction()
                Using comd As New SQLiteCommand(connstr)
                    comd.Connection = conn
                    comd.Transaction = mytransaction
                    comd.CommandText = "INSERT INTO [tblUser] (EmpID,UserName) Values (@id,@user)"
                    dtStart2 = DateTime.Now
                    Debug.Print("0==" & dtStart2)
                    For n = n0 To (n0 + 4999)
                        If n = iCount Then Exit For
                        comd.Parameters.Clear()
                        comd.Parameters.Add(New SQLiteParameter("@id", SL.Values(n)))
                        comd.Parameters.Add(New SQLiteParameter("@user", SL.Keys(n)))
                        comd.ExecuteNonQuery()
                        insertString = FormatNumber(((n * 100) / (SL.Count - 1)), 2) & " %"
                    Next
                    Debug.Print("1==" & DateTime.Now)
                    d = (DateTime.Now - dtStart2).TotalSeconds
                    Debug.Print("Diff:" & d & "Sec")
                    Debug.Print("rec:" & n)
                    'comd.Dispose()
                End Using
                mytransaction.Commit()
                'mytransaction.Dispose()
            End Using
        Next
        d = (DateTime.Now - dtStart).TotalMinutes
        Debug.Print("2==" & d)
        insert_status = "تمت عملية إدخال الأرقام"
    End Sub

المشروع مرفق Ex01.rar

#2

أنت تنفذ 4999*5000 عملية إدخال ! أي 24995000 عملية !

لا حاجة للحلقة الخارجية :

        For n0 = 0 To SL.Count - 1 Step 5000

كما يبدو لي أنك لم تقم بجعل عمود الـ EmpID مفتاح رئيسي Primary Key

لأنك لو قمت بذلك لحصلت على خطأ عند محاولة التنفيذ (إدخال أكثر من موظف بنفس المعرف)

#3
Speed_Of_Light كتب:

أنت تنفذ 4999*5000 عملية إدخال ! أي 24995000 عملية !

لا حاجة للحلقة الخارجية :

        For n0 = 0 To SL.Count - 1 Step 5000

كما يبدو لي أنك لم تقم بجعل عمود الـ EmpID مفتاح رئيسي Primary Key

لأنك لو قمت بذلك لحصلت على خطأ عند محاولة التنفيذ (إدخال أكثر من موظف بنفس المعرف)

في الحقيقة : الحلقة الخارجية لتقسيم العدد الكلي دفعات 5000 رقم في كل مرة ... و الحلقة الداخلية للتنفيذ .

أما بالنسبة للمفتاح empID فهو مفتاح رئيسي .. مع شرط التجاهل في حالة الخطأ .. و جربت مرة أخرى بدون ما أجعله مفتاح رئيسي و نفس المدة تقريباً ...

قاعدة البيانات هكذا

CREATE TABLE "tblUser" (
"EmpID"  TEXT NOT NULL,
"UserName"  TEXT,
PRIMARY KEY ("EmpID"),
CONSTRAINT "emp" UNIQUE ("EmpID") ON CONFLICT IGNORE
)

و جربتها هكذا

CREATE TABLE "tblUser" (
"EmpID"  TEXT,
"UserName"  TEXT
)

و أشكرك لك اهتمامك و ردك ...

#4

عذراً لم أفهم الكود جيداً ... فلست مبرمج VB

لكن ما فائدة الـ insertString ؟ أنت تقوم بتخزين رقم منسق فيها عند كل عملية إدخال ولا تستخدمها !

كما أن عمليات الـ debug تأخذ قسماً كبيراً من كودك ...

ولماذا تريد تقسيم العمليات 5000 كل مرة ؟ يمكنك القيام بها جميعاً بمناقلة واحدة وهذا من شأنه زيادة الأداء أيضاً

كما أنك تقوم بتحديد كل من الـ Connection والـ Transaction والـ CommandText للـ SQLiteCommand

5000 مرة، مع أنها لا تتغير كل حلقة ! ويكفي ضبطها لمرة واحدة

الكود بعد الاختصار:

Private Sub Insert(ByVal SL As SortedList)
	If conn.State = ConnectionState.Closed Then conn.Open()
	comd.Connection = conn
	comd.Transaction = mytransaction
	comd.CommandText = "INSERT INTO [tblUser] (EmpID,UserName) Values (@id,@user)"
	For n = 0 To SL.Count - 1
		comd.Parameters.Clear()
		comd.Parameters.Add(New SQLiteParameter("@id", SL.Values(n)))
		comd.Parameters.Add(New SQLiteParameter("@user", SL.Keys(n)))
		comd.ExecuteNonQuery()
	Next
	mytransaction.Commit()
	comd.Dispose()
	mytransaction.Dispose()
End Sub

تم تعديل هذه المشاركة بواسطة Speed_Of_Light في 10 أغسطس 2010 في 18:35

#5

فائدة جملة insertString تمرير نسبة الادخال (نسبة مئوية)

و جمل debug لأعرف ماذا يحدث ... فقط لاغير

و التقسيم محاولة لا غير لأن إدخال البيانات سطر سطر تتأخر بالتأكيد ، و الجميع دفعة واحدة كذلك ، فأخترت هذا الحل بناء على ما قرأت أثناء البحث عن حل في الانترنت

و إلى الآن لم أجد حلاً ... مع أني طبقت العديد من الحلول وجدتها على الانترنت و لكن لا فائدة

#6

لكنك لا تستخدم insertString !

هل جربت الكود الذي أرسلته ؟ هل يمكنك إرفاق المشروع ؟

#7

جملة insertString استخدمها في ليبل تظهر في البرنامج .

و المشروع مرفق في المشاركة الأولى .

#8

عذراً لم أنتبه للمرفقات

كما قلت لك إنك لا تستخدم insert_status

ليس هناك حاجة لـ Timer لتحديث الـ Lable

يكفي كتابة السطر التالي داخل حلقة For

Label1.Text = insert_status

بعد النظر إلى الكود يبدو لي أن السبب الرئيسي للبطئ هو اعتمادك على SortedList !

قمت بتعديل الكود بحيث يتم إدخال جميع النتائج بأقل من ثانيتين (على كمبيوتري :D )

Public Class Form1
    Private connstr As String = "Data Source=|DataDirectory|\ex0.db;Version=3;Cache_Size=2000;Page_Size=1024;Page_Count=5000;Journal_Mode=Off;Synchronous=Off;"
    Private conn As New SQLite.SQLiteConnection(connstr)
    Private Sub Insert()
        Dim d As DateTime
        d = DateTime.Now()

        If conn.State = ConnectionState.Closed Then conn.Open()
        Dim mytransaction As SQLite.SQLiteTransaction = conn.BeginTransaction()
        Dim comd As New SQLite.SQLiteCommand(connstr)
        comd.Connection = conn
        comd.Transaction = mytransaction
        comd.CommandText = "INSERT INTO [tblUser] (EmpID,UserName) Values (@id,@user)"
        For n = 1 To 150000
            comd.Parameters.Clear()
            comd.Parameters.Add(New SQLiteParameter("@id", "emp" & Rnd() * 10000))
            comd.Parameters.Add(New SQLiteParameter("@user", "user" & n))
            comd.ExecuteNonQuery()
        Next
        mytransaction.Commit()
        comd.Dispose()
        mytransaction.Dispose()

        MsgBox((DateTime.Now() - d).TotalSeconds)
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim th As New Threading.Thread(AddressOf Insert)
        th.Start()
    End Sub
End Class
1
#9

الله يرحم والديك و يبارك لك في علمك و مالك و وقتك و يرزقك من حيث لا تحتسب .. و أن يكتب لك الصحة و العافية في الدارين و أن يسكنك الفردوس الأعلى مع النبيين و الصديقين و الشهداء

كلامك عشرة على عشرة

مواضيع مشابهة