SQLite In-Memory Database (VERY FAST )

Share your advanced PureBasic knowledge/code with the community.
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

SQLite In-Memory Database (VERY FAST )

Post by pdwyer »

:shock:

The code below is basically the same as I posted before with a few very minor differences, first it uses an in-memory database instead of a file database, and second it writes a few thousand rows without a transaction to compare speed performance. The difference is impressive

It's fast enough that you could use this just because you wanted to use SQL statements on data that you have in an array or whatever, you could use SQLite as your array of structures and have access to the rich sql language to query it and get very good performance. It won't be as quick as arrays if your not doing much with them or just looping through them but if you have complex searches and queries to do it might be worth trying this to dump to an in-memory database and do some SQL queries.

Definately worth playing around with if you needs get a bit complex

Code: Select all


#SQLITE3_ISO8859         =   1   ;
#SQLITE3_OK              =   0   ;'*/ Successful result */
#SQLITE3_ERROR           =   1   ;'*/ SQL error or missing database */
#SQLITE3_INTERNAL        =   2   ;'*/ An internal logic error in SQLite */
#SQLITE3_PERM            =   3   ;'*/ Access permission denied */
#SQLITE3_ABORT           =   4   ;'*/ Callback routine requested an abort */
#SQLITE3_BUSY            =   5   ;'*/ The database file is locked */
#SQLITE3_LOCKED          =   6   ;'*/ A table in the database is locked */
#SQLITE3_NOMEM           =   7   ;'*/ A malloc() failed */
#SQLITE3_READONLY        =   8   ;'*/ Attempt to write a readonly database */
#SQLITE3_INTERRUPT       =   9   ;'*/ Operation terminated by sqlite3_interrupt() */
#SQLITE3_IOERR           =   10   ;'*/ Some kind of disk I/O error occurred */
#SQLITE3_CORRUPT         =   11   ;'*/ The database disk image is malformed */
#SQLITE3_NOTFOUND        =   12   ;'*/ (Internal Only) Table or record not found */
#SQLITE3_FULL            =   13   ;'*/ Insertion failed because database is full */
#SQLITE3_CANTOPEN        =   14   ;'*/ Unable to open the database file */
#SQLITE3_PROTOCOL        =   15   ;'*/ Database lock protocol error */
#SQLITE3_EMPTY           =   16   ;'*/ (Internal Only) Database table is empty */
#SQLITE3_SCHEMA          =   17   ;'*/ The database schema changed */
#SQLITE3_TOOBIG          =   18   ;'*/ Too much data for one row of a table */
#SQLITE3_CONSTRAINT      =   19   ;'*/ Abort due to contraint violation */
#SQLITE3_MISMATCH        =   20   ;'*/ Data type mismatch */
#SQLITE3_MISUSE          =   21   ;'*/ Library used incorrectly */
#SQLITE3_NOLFS           =   22   ;'*/ Uses OS features not supported on host */
#SQLITE3_AUTH            =   23   ;'*/ Authorization denied */
#SQLITE3_ROW             =   100  ;'*/ sqlite3_step() has another row ready */
#SQLITE3_DONE            =   101  ;'*/ sqlite3_step() has finished executing */

Global LastMessage.s
Global LibIsInitializedWithIncludedDLL.l
Global sqlite3_close.l
Global sqlite3_exec.l
Global sqlite3_open.l
Global sqlite3_errmsg.l
Global sqlite3DLL.l

Declare ProcMain()
Declare CallBack_ReturnRows(NullPtr.l,ArgCount.l,*Values.l, *ColNames.l)

; ==============================================================================

; Program Entry Point
ProcMain()

; ==============================================================================

Procedure.l ProcMain()

    SQLiteDLLPath.s = "sqlite3.dll" 
    DataBaseFile.s = ":memory:" ;"test0001.dat" ;

    ;Load SQLite DLL
    sqlite3DLL = LoadLibrary_(@SQLiteDLLPath)
        sqlite3_close       = GetProcAddress_(sqlite3DLL, "sqlite3_close")
        sqlite3_exec        = GetProcAddress_(sqlite3DLL, "sqlite3_exec")
        sqlite3_open        = GetProcAddress_(sqlite3DLL, "sqlite3_open")
        sqlite3_errmsg      = GetProcAddress_(sqlite3DLL, "sqlite3_errmsg")
    
    ;Prep SQL statements
    CreateDB.s =            "CREATE TABLE 'MyTable' (" + #CRLF$ 
    CreateDB = CreateDB +   "'ID' INTEGER  Not NULL PRIMARY KEY AUTOINCREMENT," + #CRLF$
    CreateDB = CreateDB +   "'StringCol1' TEXT  NULL," + #CRLF$
    CreateDB = CreateDB +   "'StringCol2' TEXT  NULL," + #CRLF$
    CreateDB = CreateDB +   "'IntCol1' INTEGER  NULL," + #CRLF$
    CreateDB = CreateDB +   "'IntCol2' INTEGER  NULL" + #CRLF$
    CreateDB = CreateDB +   ")"    
    CreateDB = ReplaceString(CreateDB, "'", #DOUBLEQUOTE$)

    SQLAddRow.s = "Insert into MyTable(StringCol1, StringCol2, IntCol1, IntCol2) values('Hello', 'NextColumn', 100, 12345678) "

    CurrentTime.f = ElapsedMilliseconds()

    ;Open Database
    If CallCFunctionFast(sqlite3_open, DataBaseFile, @dbHandle)
        CallCFunctionFast(sqlite3_errmsg, @RetVal)
        Debug = PeekS(RetVal)
    Else
        ; If successful send queries

        ; Send Created DB SQL, table may exist
        QueryRet.l = CallCFunctionFast(sqlite3_exec, DBHandle, CreateDB, @CallBack_ReturnRows() , #Null, @ReturnValue)      
        If QueryRet
            CallCFunctionFast(sqlite3_errmsg, @ReturnValue)
            Debug = PeekS(ReturnValue)
        EndIf
        
        ; Send Insert Row SQL, table may exist
        For i.l = 1 To 5000
        QueryRet.l = CallCFunctionFast(sqlite3_exec, DBHandle, SQLAddRow, @CallBack_ReturnRows() , #Null, @ReturnValue) 
            If QueryRet
                CallCFunctionFast(sqlite3_errmsg, @ReturnValue)
                Debug = PeekS(ReturnValue)
            EndIf       
        Next
        ;Send arbitrary Query
        QueryRet.l = CallCFunctionFast(sqlite3_exec, DBHandle, "Select * from MyTable", @CallBack_ReturnRows() , #Null, @ReturnValue) 
        If QueryRet
            CallCFunctionFast(sqlite3_errmsg, @ReturnValue)
            Debug = PeekS(ReturnValue)
        EndIf 

        ;Close Database
        CallCFunctionFast(sqlite3_close, DBHandle)
    
    EndIf

    Debug  ElapsedMilliseconds() - CurrentTime 

    ;Unload DLL on program end
    FreeLibrary_(sqlite3DLL)

EndProcedure

;==============================================================================

ProcedureC.l CallBack_ReturnRows(NullPtr.l,ArgCount.l,*Values.l, *ColNames.l) ;argcount = column count
  
    Dim ValuePts.l(ArgCount)
    Row.s = ""

    For ValLoop.l = 0 To ArgCount -1
        ValuePts(ValLoop) = PeekL(*Values + (ValLoop * 4)) 
        Row = Row + PeekS(ValuePts(ValLoop) ) + "," 
    Next
        
    Debug Row
        
    ProcedureReturn = 0  ;Proceed with next row

EndProcedure                               

;======================================================================================


Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

feel free to comment out "Debug Row " in that last callback proc to just get the times, on my PC it was 187ms vs 21sec (115x speedup) over a disk file. That debug statement is there just to show you that the data did get there and is queriable, it didn't just fly off into space :)


:!: :arrow: Another point to note, since the PBOSL version of SQLite does a file check before creating or opening a database...

Code: Select all

If FileSize(sDataBase) = -1 
or
If FileSize(sDataBase) <> -1
I don't think in-memory databases will be usable. Something to consider for the next version perhaps
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

I'm still working on the first example and you post a second?

Seriously, good stuff here that I'm attempting to learn from.

We do use sqlite from memory, using either SQLite3_InitLibFromMemory(?SQLite3_DLL) or sqlite3_lib = LoadLibraryM(?SQLite3_DLL) depending on the lib you choose.

Since you're fast becoming another one of my 'heros' :) I have a practical sqlte issue I'm currently attempting to handle. I need to create and load a table from a 1GB database of text and images (the images account for most of the size. There are only about 1500 records in the database). The extracted table is between 400 and 500 columns which I then parse by row, extracting relevant data to a linked list via -

SQLite3_GetTable("select * from table where name = "dwyer"
then First_row, Next_row to read the rows.

Then get the row data by index, which uses
*Recordset\sValue = PeekS(PeekL(*Recordset\Handle + (lindex * 4) + (*Recordset\CurrentPos * *Recordset\Cols) * 4))

There are eight rows extracted - all are text.
This seems similar to what you're doing.

My question/concern is that it can take upward of 15 seconds for the above mentioned table to be created and the relevant data extracted. This being my only real experience with sqlite, I don't know whether that is 'normal' for an extraction of that nature or whether I could improve it. Might you have an opinion/suggestions?

Also, would you know if sqlite frees it's own memory following a SQLite3_Column_Blob(Statement, n)? I've been able to find 'speculation' that it does via a google search, but was wondering if you might know (since I have a memory leak and want to eliminate sqlite3 blob from consideration, or free the memory, if I need to).

Many thanks for your contributions to the forums. The more sqlite knowledge the better :)

cheers

whoops - hope it's ok to ask a coding question in 'tricks and tips' :oops:
Last edited by rsts on Sun May 20, 2007 5:33 pm, edited 1 time in total.
Dare
Addict
Addict
Posts: 1965
Joined: Mon May 29, 2006 1:01 am
Location: Outback

Post by Dare »

rsts wrote:I'm still working on the first example and you post a second?
:D
rsts wrote:Many thanks for your contributions to the forums.
Agreed. Almost makes up for introducing that ruddy game .....

Keep the code coming. Like rsts I am not getting through them as fast as you're producing them but it all helps.

Now all we need is a workaround for the missing sqlite "DROP COLUMN" .. :D
Dare2 cut down to size
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

rsts wrote:We do use sqlite from memory, using either SQLite3_InitLibFromMemory(?SQLite3_DLL) or sqlite3_lib = LoadLibraryM(?SQLite3_DLL) depending on the lib you choose.
You might have missed the point, he's got the table in memory, not the library:

Code: Select all

DataBaseFile.s = ":memory:"
BERESHEIT
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Huh, I didn't know that SQLite could work with memory based databases like this! You learn something new every day. :)

Thanks pdwyer.
I may look like a mule, but I'm not a complete ass.
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

netmaestro wrote:
rsts wrote:We do use sqlite from memory, using either SQLite3_InitLibFromMemory(?SQLite3_DLL) or sqlite3_lib = LoadLibraryM(?SQLite3_DLL) depending on the lib you choose.
You might have missed the point, he's got the table in memory, not the library:

Code: Select all

DataBaseFile.s = ":memory:"
Duh - you caught me there :oops:
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

rsts, in regards to your question, I'm not quite sure. One thing I want to play with if I get any time this week is using blobs and returning the binary data in there. I know it's doable but I've never needed to do it so I've never looked into what the return data looks like.

15secs sounds like a long time to return for 8 rows, but the comment about 400-500 columns sounds like it might be an issue.

Still, 1gb database with only 1500 rows says that these rows can be very large (from the image size I guess). How big does the process get in memory when you run this? Maybe it's paging back to disk
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
Dare
Addict
Addict
Posts: 1965
Joined: Mon May 29, 2006 1:01 am
Location: Outback

Post by Dare »

At first I thought this (the in-memory db) was nice and novel, but I couldn't really see how it would be all that useful versus arrays and lists with a little b-tree or binary-lookup tweaking, etc.

On consideration though, this can very handy indeed! I am very glad you brought this to our attention.

Thanks mate!
Dare2 cut down to size
dige
Addict
Addict
Posts: 1391
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Post by dige »

@pdwyer: thank you very much, that opens up completely new possibilities!!
thanos
Enthusiast
Enthusiast
Posts: 423
Joined: Sat Jan 12, 2008 3:25 pm
Location: Greece
Contact:

Post by thanos »

Is it possible to save the virtually created database (":memory:") to disk as a real SQLite database?
I mean with a copy or save memory procedure to be as fast as possible.
Regards.

Thanos
» myPersonal Banker :: Because you do not need to have a master degree in economics in order to organize your finances!
User avatar
Kiffi
Addict
Addict
Posts: 1486
Joined: Tue Mar 02, 2004 1:20 pm
Location: Amphibios 9

Post by Kiffi »

thanos wrote:Is it possible to save the virtually created database (":memory:") to disk as a real SQLite database?
I mean with a copy or save memory procedure to be as fast as possible.
http://www.purebasic.fr/english/viewtop ... ory#248007

Greetings ... Kiffi
Hygge
thanos
Enthusiast
Enthusiast
Posts: 423
Joined: Sat Jan 12, 2008 3:25 pm
Location: Greece
Contact:

Post by thanos »

Kiffi wrote:
thanos wrote:Is it possible to save the virtually created database (":memory:") to disk as a real SQLite database?
I mean with a copy or save memory procedure to be as fast as possible.
http://www.purebasic.fr/english/viewtop ... ory#248007

Greetings ... Kiffi
Thanks Kiffi!

Regards.

Thanos
» myPersonal Banker :: Because you do not need to have a master degree in economics in order to organize your finances!
thanos
Enthusiast
Enthusiast
Posts: 423
Joined: Sat Jan 12, 2008 3:25 pm
Location: Greece
Contact:

Post by thanos »

Short question:
Is SQlite useful in multi user environments?
I mean with record locking etc.
Regards.

Thanos
» myPersonal Banker :: Because you do not need to have a master degree in economics in order to organize your finances!
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

not really, no.

You'd need a middle tier to serialise the requests. selects can run together but updates and inserts etc can't
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
Post Reply