Page 1 of 2

In Memory SQLite DB (PB 4.20)

Posted: Fri May 30, 2008 11:28 am
by pdwyer
Actually I've never used PB's DB engine but now that 4.20 full version is out with SQLite I thought it's time to see if I can retire my include files and use native PB for all this.

Test 1 was Japanese in Unicode mode and PB SQLite was fine "out of the box" (but not much interesting to post in the tips area there). Next is InMemory Databases.

This works fine too :)

Next I guess will be blobs and X' sql syntax I hope! Thanks PB Team for the new tools

Code: Select all

    UseSQLiteDatabase()

    If OpenDatabase(0, ":memory:", "", "")
        Debug "Connected"
        
        DatabaseUpdate(0, "CREATE TABLE MyTable (test VARCHAR(255));")       
        Debug DatabaseError()
        
        For i = 1 To 15
            DatabaseUpdate(0, "insert into MyTable (test) values('Hello Row " + Str(i)+ "')")
        Next
        
        DatabaseQuery(0, "SELECT * FROM MyTable")
        While NextDatabaseRow(0)
            Debug GetDatabaseString(0, 0)
        Wend
    
    Else
        Debug DatabaseError()    
    EndIf

Posted: Fri May 30, 2008 12:23 pm
by srod
Now that is good to know - that the in memory database carries over to the PB library.

Thanks. :)

Posted: Fri May 30, 2008 12:51 pm
by akj
Please could the ":memory:" feature be documented within the help page for the UseSQLiteDatabase() command?
It would be helpful for newcomers to SQLite.

Posted: Fri May 30, 2008 1:06 pm
by srod
It is an SQLite feature really rather than a PB one.

Posted: Fri May 30, 2008 1:41 pm
by pdwyer
It's hard to find docs about it on the sqlite.org site too which is unlike them, I found it a while back only because they made a change and it popped up in their announcements about it. I don't see it in their API docs section, just in their wiki discussions and CVS blogs etc

Posted: Fri May 30, 2008 1:52 pm
by srod
I originally found it in their api docs somewhere - but I couldn't tell you where right now! :)

**EDIT : actually, I tell a lie; the first time I encountered it was in another thread of yours! :wink: However I have since seen it in the SQLite docs.

Posted: Fri May 30, 2008 1:59 pm
by netmaestro
Good tip, thanks!

Posted: Tue Jun 03, 2008 5:27 pm
by Karbon
I wish the SQLite command line utility's ".dump" command worked through the API. That way we could save SQLite files to text and import them directly back into a memory database..

Posted: Wed Jun 11, 2008 2:10 am
by superadnim
So it's not possible to save anything that we made in memory?

On a side-note: I would love to have compression support, but its a feature the author of sqlite asks money for :?

Posted: Wed Jun 11, 2008 4:41 am
by pdwyer
I think I saw this get requested within the sqlite world but it doesn't look to be happening.

I think if you want data out of an in memory DB you need to select it out (which is very fast from memory) and write it to a disk db.

I haven't tried to open multiple DBs at the same time with SQLite (only multiple tables) so I'm not sure if you can use "Select Into" with DB.TABLE or not. That would be a nice easy way I guess.

I think (guessing) that since in memory dbs are not as safe (in that data is lost if the process dies) it's more of a feature for a different type of application (a way to do complex searches on complex memory structures easily) rather than a performance tweak for a DB that needs to be kept on disk.

For example, a little commandline app that loads a CSV file and a TSV to memory, does a select with a join and then outputs a quick query result and terminates. There would be a lot of code to saved using SQL syntax and little need for disk I/O overhead of normal DBs.

I admit, it would be nice to dump an in-memory db though :P

Posted: Sat Jun 14, 2008 8:58 pm
by Amnesty
Its an amazing trick, this :memory: function, thank you for posting.

Its so fast, you can create your own OLAP System. ;-)

Its a pity that its so slowly writing, because I dont know how to use the ".import" SQLite command in PureBasic. Maybe somebody has an idea, because its the fastest way to write DB from memory back into file. As far as I know.

regards

Amnesty

Code: Select all

Structure table
  sql.s
  name.s
EndStructure

Procedure OpenSQLiteDatabase2Mem(database.l, file.s, user.s, pass.s)
  NewList createStatement.table()
  
  source = OpenDatabase(#PB_Any, File, User, Pass, #PB_Database_SQLite)
  If source
    DatabaseQuery(source, "Select sql, name FROM sqlite_master WHERE type='table'")
    While NextDatabaseRow(source)
      AddElement(createStatement())
      createStatement()\sql  = GetDatabaseString(source, 0)
      createStatement()\name = GetDatabaseString(source, 1)
    Wend
    If OpenDatabase(database, ":memory:", "", "", #PB_Database_SQLite)
      ForEach createStatement()
        DatabaseUpdate(database, createStatement()\sql)
        DatabaseQuery(source, "select * from " + createStatement()\name)  
        columns = DatabaseColumns(source)
        While NextDatabaseRow(source)
          values.s = ""
          For columnNr = 0 To columns - 1
            If DatabaseColumnType(source, columnNr) = #PB_Database_String Or DatabaseColumnType(source, columnNr) = 0 
              values+ "'" + GetDatabaseString(source,columnNr) + "'"
              Else
              values+ GetDatabaseString(source,columnNr)
            EndIf
            If columnNr < columns -1
              values+ ","
            EndIf
          Next
          DatabaseUpdate(database, "insert into " + createStatement()\name + " values(" + values + ")")
        Wend
      Next
      Else
        MessageRequester("Error","Can't open DB.",0) 
        ProcedureReturn
    EndIf
    CloseDatabase(source)
    Else
      MessageRequester("Error","Can't open DB.",0) 
      ProcedureReturn
  EndIf
EndProcedure

Procedure WriteSQLiteDatabase2Disk(source.l, File.s)
  NewList createStatement.table()

  destinationFile = CreateFile(#PB_Any, File.s)
  If file
    CloseFile(destinationFile)
    Else
      MessageRequester("Error","Can't write DB.",0) 
      ProcedureReturn
  EndIf
  
  destinationDB = OpenDatabase(#PB_Any, File, "", "", #PB_Database_SQLite)
  If destinationDB
    DatabaseQuery(source, "Select sql, name FROM sqlite_master WHERE type='table'")
    While NextDatabaseRow(source)
      AddElement(createStatement())
      createStatement()\sql  = GetDatabaseString(source, 0)
      createStatement()\name = GetDatabaseString(source, 1)
    Wend
    ForEach createStatement()
      DatabaseUpdate(destinationDB, createStatement()\sql)
      DatabaseQuery(source, "select * from " + createStatement()\name)  
      columns = DatabaseColumns(source)
      While NextDatabaseRow(source)
        values.s = ""
        For columnNr = 0 To columns - 1
          If DatabaseColumnType(source, columnNr) = #PB_Database_String Or DatabaseColumnType(source, columnNr) = 0 
            values+ "'" + GetDatabaseString(source,columnNr) + "'"
            Else
            values+ GetDatabaseString(source,columnNr)
          EndIf
          If columnNr < columns -1
            values+ ","
          EndIf
        Next
        DatabaseUpdate(destinationDB, "insert into " + createStatement()\name + " values(" + values + ")")
      Wend
    Next
    CloseDatabase(destinationDB)
    Else
      MessageRequester("Error","Can't open DB.",0) 
      ProcedureReturn
  EndIf
EndProcedure


UseSQLiteDatabase()

OpenSQLiteDatabase2Mem(0,"c:\PureBasic.sqlite","","")

WriteSQLiteDatabase2Disk(0, "c:\purebasic_new.sqlite")


Posted: Sat Jun 14, 2008 9:13 pm
by srod
Nice example - thanks. 8)

Posted: Sun Jun 15, 2008 12:53 am
by Kiffi
Amnesty wrote:Maybe somebody has an idea
perhaps this is faster (by using Attach Database). Can you test it?

Code: Select all

EnableExplicit

Procedure.s GetTableNames(hDB.l)             ; Returns the tablenames of the specified database
  
  Protected TableName.s
  
  DatabaseQuery(hDB, "Select tbl_name From sqlite_master Where type='table' Order By tbl_name")
  
  While NextDatabaseRow(hDB)
    TableName + GetDatabaseString(hDB, 0) + ";"
  Wend
  
  If Right(TableName,1)=";"
    TableName = Left(TableName,Len(TableName)-1)
  EndIf
  
  ProcedureReturn TableName
  
EndProcedure

Procedure.s GetSchema(hDB.l, TableName.s)   ; Returns a schema of a table
  
  Protected SQL.s
  Protected Schema.s
  
  SQL + "SELECT sql FROM "
  SQL + "(SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) "
  SQL + "WHERE tbl_name = '" + TableName + "' AND type!='meta' "
  SQL + "ORDER BY type DESC, name "
  
  DatabaseQuery(hDB, SQL)
  
  While NextDatabaseRow(hDB)
    Schema + GetDatabaseString(hDB, 0)
  Wend
  
  ProcedureReturn Schema
  
EndProcedure

; MemoryDb to FileDb

Define hDB.l
Define Counter.l
Define FF.l
Define MemoryTablenames.s
Define MemoryTablename.s
Define Schema.s

UseSQLiteDatabase()

hDB = OpenDatabase(#PB_Any, ":memory:", "", "", #PB_Database_SQLite)
  
If hDB
  
  ; create a sample-table
  DatabaseUpdate(hDB, "Create Table TestTable (TestField)")
  
  ; insert some sample-data
  For Counter = 0 To 9
    DatabaseUpdate(hDB, "Insert Into TestTable (TestField) Values ('TestValue" + Str(Counter) + "')")
  Next
  
  ; create an empty sqlite-database
  FF = CreateFile(#PB_Any, "D:\FromMemoryToDisk.db")
  
  If FF = 0
    MessageRequester("", "Couldn't create D:\FromMemoryToDisk.db")
    End
  EndIf
  
  CloseFile(FF)
  
  DatabaseUpdate(hDB, "Attach Database 'D:\FromMemoryToDisk.db' As M2D_DB")
  
  ; Get all tablenames
  MemoryTablenames = GetTableNames(hDB)
  
  For Counter = 0 To CountString(MemoryTablenames, ";")
    
    ; get each Memorytablename from MemoryTablenames-'Collection'
    MemoryTablename = StringField(MemoryTablenames, Counter, ";")
    
    ; get schema from the current table
    Schema = GetSchema(hDB, MemoryTablename)
    
    ; create file-table
    Schema = ReplaceString(Schema, "CREATE TABLE " + MemoryTablename, "CREATE TABLE M2D_DB." + MemoryTablename)
    DatabaseUpdate(hDB, Schema)
    
    ; Insert content from memory-table to file-table
    DatabaseUpdate(hDB, "INSERT INTO M2D_DB." + MemoryTablename + " Select * FROM " + MemoryTablename)
    
  Next Counter
  
  CloseDatabase(hDB)
  
EndIf
Greetings ... Kiffi

// Edit: You can also try to encapsulate the Insert-Statements
into a Transaction.

Posted: Sun Jun 15, 2008 1:37 am
by Edwin Knoppert
superadnim wrote:So it's not possible to save anything that we made in memory?

On a side-note: I would love to have compression support, but its a feature the author of sqlite asks money for :?
Sure by using drop table and then save to the desired tablename using INSERT INTO it's a piece of cake..

Sqlite is versatile, espec due it's library size being so small.

PS,
Blobs through x'' notation will do fine as well.

Posted: Sun Jun 15, 2008 3:39 am
by pdwyer
Amnesty wrote:Its a pity that its so slowly writing
SQLite is always slower to write (although faster with memory dbs of course) as it flushes caches etc after a transaction. This makes inserts in big loops very slow on disk DBs.

If, however you use a transaction to insert all your speed comes back :).
http://www.sqlite.org/cvstrac/wiki?p=Pe ... iderations

In my tests, if I put about 500 inserts per transaction I get best performance but this may very aon the size of the insert statement text. If I go too high with like 5000 inserts then the string concaternation starts to slow it down again. Perhaps if my string building was better optimised that might not happen.