Open seal713 opened 9 years ago
For now, we store/retrieve the data as a JSON string to/from WebSQL db. Every time, we have to rebuild the schema. Is there any way we can store and reuse the schema info as well?
@seal713
Please provide an example of how are you store data before you put them into the table to help us identify the problem.
(bonusinfo: if you have them as a array of arrays per table you can do: alasql.tables.tablename.data = mySoruceObj
)
@mathiasrw
Many thanks for your kindly feedback. Here is the sample codes which we are using.
The alasql.tables.tablename.data stores data that can be loaded quickly. The major issue is the schema creation takes much time. We want to know if the schema creation we did here can be reused or is there anyway to improve the performance for from Alasql end.
Here is the sample code for reference.
alasqlDB = new alasql.Database("alasql_db");
// creating schema is taking much time for all the 70 tables
// is it possible to fastly restore the schema (saved in localstorage) without executing SQL?
var tableSchemas = JSON.parse(window.localStorage.getItem('TableSchemas'))
for(table in tableSchemas) {
// get the SQL from local storage, for example,
// 'CREATE TABLE [cities] ([id] INT, city TEXT DEFAULT "test", population INT DEFAULT 0, CONSTRAINT AB UNIQUE(id));'
var createquery = tableSchemas[table];
alasqlDB.exec(createquery); // the exec takes time
}
// data load
for(all the <tableName> get the <content> from storage) {
var myarray = JSON.parse(content);
alasqlDB.exec('INSERT INTO [' + tableName + '] SELECT * FROM ?', [myarray]);
}
It does not make much sense. The scheme should be the quick part.
if you would like to share your full tableSchemas that would be awesome.
I am not aware of how to copy the schemas but have a feeling something in the schemas is blocking the normally speedy process.
@mathiasrw
Yes, in desktop browser it is fast, but in ipad Safari, the performance degrade so much. See below one for example, we create 100 tables(each around 100 columns).
When you test on desktop, it takes around 3 seconds, but in iPad, it takes around 25 seconds. That's why we want to know whether Alasql has anyway to store the schema. And when we needed, we can load it directly, not to create again.
Codes sample:
var NUM = 100;
for(var i=0;i<NUM;i++)
{
var sql_createtbl = 'CREATE TABLE test' + i + ' (RecordNum INTEGER PRIMARY KEY AUTOINCREMENT , [col1] TEXT DEFAULT "", [col2] TEXT DEFAULT "", [col3] TEXT DEFAULT "", [col4] TEXT DEFAULT "", [col5] TEXT DEFAULT "", [col6] TEXT DEFAULT "", [col7] TEXT DEFAULT "", [col8] TEXT DEFAULT "", [col9] TEXT DEFAULT "", [col10] TEXT DEFAULT "", [col11] TEXT DEFAULT "", [col12] TEXT DEFAULT "", [col13] TEXT DEFAULT "", [col14] TEXT DEFAULT "", [col15] TEXT DEFAULT "", [col16] TEXT DEFAULT "", [col17] TEXT DEFAULT "", [col18] TEXT DEFAULT "", [col19] TEXT DEFAULT "", [col20] TEXT DEFAULT "", [col21] TEXT DEFAULT "", [col22] TEXT DEFAULT "", [col23] TEXT DEFAULT "", [col24] TEXT DEFAULT "", [col25] TEXT DEFAULT "", [col26] TEXT DEFAULT "", [col27] TEXT DEFAULT "", [col28] TEXT DEFAULT "", [col29] TEXT DEFAULT "", [col30] TEXT DEFAULT "", [col31] TEXT DEFAULT "", [col32] TEXT DEFAULT "", [col33] TEXT DEFAULT "", [col34] TEXT DEFAULT "", [col35] TEXT DEFAULT "", [col36] TEXT DEFAULT "", [col37] TEXT DEFAULT "", [col38] TEXT DEFAULT "", [col39] TEXT DEFAULT "", [col40] TEXT DEFAULT "", [col41] TEXT DEFAULT "", [col42] TEXT DEFAULT "", [col43] TEXT DEFAULT "", [col44] TEXT DEFAULT "", [col45] TEXT DEFAULT "", [col46] TEXT DEFAULT "", [col47] TEXT DEFAULT "", [col48] TEXT DEFAULT "", [col49] TEXT DEFAULT "", [col50] TEXT DEFAULT "", [col51] TEXT DEFAULT "", [col52] TEXT DEFAULT "", [col53] TEXT DEFAULT "", [col54] TEXT DEFAULT "", [col55] TEXT DEFAULT "", [col56] TEXT DEFAULT "", [col57] TEXT DEFAULT "", [col58] TEXT DEFAULT "", [col59] TEXT DEFAULT "", [col60] TEXT DEFAULT "", [col61] TEXT DEFAULT "", [col62] TEXT DEFAULT "", [col63] TEXT DEFAULT "", [col64] TEXT DEFAULT "", [col65] TEXT DEFAULT "", [col66] TEXT DEFAULT "", [col67] TEXT DEFAULT "", [col68] TEXT DEFAULT "", [col69] TEXT DEFAULT "", [col70] TEXT DEFAULT "", [col71] TEXT DEFAULT "", [col72] TEXT DEFAULT "", [col73] TEXT DEFAULT "", [col74] TEXT DEFAULT "", [col75] TEXT DEFAULT "", [col76] TEXT DEFAULT "", [col77] TEXT DEFAULT "", [col78] TEXT DEFAULT "", [col79] TEXT DEFAULT "", [col80] TEXT DEFAULT "", [col81] TEXT DEFAULT "", [col82] TEXT DEFAULT "", [col83] TEXT DEFAULT "", [col84] TEXT DEFAULT "", [col85] TEXT DEFAULT "", [col86] TEXT DEFAULT "", [col87] TEXT DEFAULT "", [col88] TEXT DEFAULT "", [col89] TEXT DEFAULT "", [col90] TEXT DEFAULT "", [col91] TEXT DEFAULT "", [col92] TEXT DEFAULT "", [col93] TEXT DEFAULT "", [col94] TEXT DEFAULT "", [col95] TEXT DEFAULT "", [col96] TEXT DEFAULT "", [col97] TEXT DEFAULT "", [col98] TEXT DEFAULT "", [col99] TEXT DEFAULT "", [col100] TEXT DEFAULT "", [col101] TEXT DEFAULT "", [col102] TEXT DEFAULT "", [col103] TEXT DEFAULT "")';
db.exec(sql_createtbl);
}
THank you for the demo code.
Interesting.
I will investigate...
@mathiasrw ,
Recently days, we've tried one option, to store below properties for each table into local storage and reload them from local storage when needed.
var keysImdbTable = ['columns', 'xcolumns', 'defaultfns', 'identities', 'inddefs', 'indices', 'uniqdefs', 'checkfn'];
But we met one issue, the 'UNIQUE' constraint is not working properly and it may have some other potential issue as well.
Hence we wonder is there any way from Alasql end which can make the DDL execution faster, or any similar like above but more robust API we can use to store/load the schema.
Warm regards, Seal
@seal713
I have not found out any good way. Mayby @agershun can help out - but he is not so active these weeks.
@mathiasrw @agershun ,
Many thanks for your kindly support. Please let us know once there is any update from your guys expertise.
Regards, Seal
A potential workaround that I've been using is to define the schema once, then dump it as a JSON and save it. Then you can simply load the schema from that. So
var myDB = JSON.stringify(alasql.databases[dbid])
Save the output (localStorage, on the server, indexDB) Then, when the page loads, instead of defining the ddl, you can simply do the following
alasql.databases[dbid] = JSON.parse(myDB);
But it seems like you've already tried that (not sure)
The best way to improve serialization/deserialization robustness is probably to add specialized toJSON methods. See: Object#toJSON
Then add specialized static methods fromJSON methods and recursively handle each prototype.
Interesting
Would you mind sharing the table definitions? I would love to test what part of them that affects time
@nickdeis @mathiasrw , thanks a lot for you guys assistance. But now the issue is, once we reload the database with alasql.databases[dbid] = JSON.parse(myDB);, some constraint schema like UNIQUE is missing.
Looks like those kind of constraint information is missing or can not be re-initialized during the loading. This may need alasql's fmk change.
Yes, there some thing to be done after Database is loading from JSON:
Some ideas can be borrowed from here
I will add a special function to AlaSQL for storing/restoring whole database from the JSON object. We also need this functionality for TRANSACTIONs support. Sorry for late answer.
I will add a special function to AlaSQL for storing/restoring whole database from the JSON object
Would really be a killer feature
Some ideas can be borrowed from here
Interesting comment on https://github.com/agershun/alasql/blob/develop/src/92localstorage.js#L71 :)
We also need to do not forget about 'objects'... This property is used by graph functionality.
Отправлено с iPhone
11 янв. 2016 г., в 15:04, Mathias Rangel Wulff notifications@github.com написал(а):
I will add a special function to AlaSQL for storing/restoring whole database from the JSON object
Would really be a killer feature
— Reply to this email directly or view it on GitHub.
:+1: This would help my alasql-loader and any build time/ serverside technologies in the future.
Absolutely! I am starting...
How are you thinking the syntax will be?
I was thinking something simple like
IMPORT('./myData.db.json');
...
update abc set bla = 45 where xxx=123;
...
EXPORT('./myData.db.json');
or maybe
LOAD('./myData.db.json');
...
update abc set bla = 45 where xxx=123;
...
SAVE('./myData.db.json');
Might be of some use: Object#toJSON
I'd rather see a alasql.load(json) and alasql.dump(db) function. Could always have both I suppose.
Good point. Its must be accessed by a property of the alasql object.
I actually prefer your words .load(
and .dump(
I beleave its good to have it as a "sql" command syntax also - so it can be used from the commandline
Just dive into the issue... we will have a problem to restore insert/delete/update functions...
You can see some temporary files:
... will work on it...
STORE
/RESTORE
:) serialize
/deserialize
. But it does not matter...var obj = alasql.storeDatabase(databaseid)
alasql.restoreDatabase(obj,databaseid,forceOverwrite)
I would hope that there was (also) a method to export all of my databases at the same time so it's only one operation when I want to save all of my alasql on the client.
var obj = alasql.storeDatabase(databaseid); alasql.restoreDatabase(obj,databaseid,forceOverwrite)
In my perception it does not make sense not to force overwrite. I cant se when you would NOT like to force an overwrite...
What about if
JSON.stringify(alasql)
Just dive into the issue... we will have a problem to restore insert/delete/update functions...
are you talking triggers?
As long as the triggers are made by making a function that observes (so its not SQL stored in alasql) I see no reason to keep this as part of the dump.
No, Triggers do not use observers. So the problem not here, but in createtable.js file. AlaSQL creates three functions for each table. Probably, I can reqrite this procedure to unlink it from the table...
The import/export/load/dump/store/restore/serialize/deserialize/ feature is also a bit related to #54 and #110
BTW some people need the function to 'flat the JSON object', like (there are some XLS-related questions about it):
var obj = {a:{b:{c:5}, d:1}, c:2};
var res = flat(obj);
// res = {'a.b.c':5,'a.d':1,c:2};
Did you meet this function before?
We can add it to the library with parameters:
function FLAT(obj, pointer) {}
function UNFLAT(obj, pointer) {}
By default pointer
is equal "."
So, we can use it:
var data = [{a:{b:{c:1}}}, {a:{d:5}}];
alasql('SELECT * INTO XLSX("my.xlsx",{header:true}) FROM FLAT(?,"->")',[data]);
// save data in XLSX file:
// a->b->c a->d
// 1
// 5
Nope. I have a feeling its a recursive little cute function. I think the hard part is to make sure one does not go into a loop with objects somehow pointing to a parrent
What about if there is an object with an array of objects - do they expect the flat
to do create elements like a.b[5].foo
?
Yes. a.b[5]
or a.b.5
. This can be an option. It is not hard (but little bit slow to check for nested recursion - but it solvable). I atill thinking about this StackOverflow issue: http://stackoverflow.com/questions/34527959/parse-a-jsonwith-array-and-objects-and-export-the-data-into-excel-file-in-node/34771859#34771859
Made the flat
function: http://jsfiddle.net/40oq02g4/
Im expanding with abillity to config joining chars
Yes. a.b[5] or a.b.5.
If its .5
one does not know if it was an array or a n object property named "5"
You are great programmer!
a[5] or a.5
- Who cares... :) anyway it can be reached by a['b']['5']
We can make the secial option or leave it as a.b[5]
. Let's use [ ]
and then ask users for that.
Could you write a FROM function from the FLAT() function like:
alasql.stdfn.FLAT = function() {
// Your function goes here
}
alasql.from.FLAT = function(filename, opts, cb, idx, query) {
var res = alasql.stdfn.FLAT(filename,opts);
if(cb){
res = cb(res, idx, query);
}
return res;
};
After that we can use this function:
SELECT FLAT({a:{b:1}})
SELECT * FROM FLAT({a:{b:1}})
Opdated version: http://jsfiddle.net/40oq02g4/1/
var flat = function(obj, objDevider, levels, showArrayAsObjec, arrayDevider){
levels = levels | 0 || 100;
objDevider = objDevider || '.';
arrayDevider = arrayDevider || [];
arrayDeviderPre = arrayDevider[0] || '[';
arrayDeviderPost = arrayDevider[1] || ']';
showArrayAsObjec = !!showArrayAsObjec;
function flatten(obj, path, result, level, parrentIsObj) {
if(level>levels){
return;
}
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var name
if(0===level){
name = prop
} else if (parrentIsObj || showArrayAsObjec){
name = path + objDevider + prop
}else{
name = path + arrayDeviderPre +prop+arrayDeviderPost
}
if (typeof obj[prop] == "object"){
flatten(obj[prop], name, result, level+1, obj[prop].constructor !== Array);
}else{
result[name] = obj[prop]
}
}
}
return result
}
return flatten(obj, '', {}, 0);
}
Default settings are the same as flat(obj, '.', 100, false, ['[',']']);
Having
var x = {
'name': 'a',
'kid': {
'level': 2,
'sun': [{
'name': 'c',
'level': 3
},{
'name': 'b',
'level': 2,
'sun': [{
'name': 'c',
'level': 3
}]
}]}
}
we get
flat(x, '->');
{
"name": "a",
"kid->level": 2,
"kid->sun[0]->name": "c",
"kid->sun[0]->level": 3,
"kid->sun[1]->name": "b",
"kid->sun[1]->level": 2,
"kid->sun[1]->sun[0]->name": "c",
"kid->sun[1]->sun[0]->level": 3
}
flat(x, '->');
gives
{
"name": "a",
"kid->level": 2,
"kid->sun[0]->name": "c",
"kid->sun[0]->level": 3,
"kid->sun[1]->name": "b",
"kid->sun[1]->level": 2,
"kid->sun[1]->sun[0]->name": "c",
"kid->sun[1]->sun[0]->level": 3
}
flat(x, '_', 3, true)
gives
{
"name": "a",
"kid_level": 2,
"kid_sun_0_name": "c",
"kid_sun_0_level": 3,
"kid_sun_1_name": "b",
"kid_sun_1_level": 2
}
Could you write a FROM function from the FLAT() function like:
I expect that 'filename' can be renamed to object and that the options are an object with settings?
We were (I was)wrong. A little bit. I will describe it in an hour.
Отправлено с iPhone
14 янв. 2016 г., в 20:32, Mathias Rangel Wulff notifications@github.com написал(а):
Could you write a FROM function from the FLAT() function like:
I expect that 'filename' can be renamed to object and that the options are an object with settings
— Reply to this email directly or view it on GitHub.
For array we need to generate a separate line for each array element with all upper properties
Отправлено с iPhone
14 янв. 2016 г., в 20:40, agershun@gmail.com написал(а):
We were (I was)wrong. A little bit. I will describe it in an hour.
Отправлено с iPhone
14 янв. 2016 г., в 20:32, Mathias Rangel Wulff notifications@github.com написал(а):
Could you write a FROM function from the FLAT() function like:
I expect that 'filename' can be renamed to object and that the options are an object with settings
— Reply to this email directly or view it on GitHub.
Try this from http://jsfiddle.net/40oq02g4/2/
alasql.stfn.FLAT = function(obj,opts) {
opts = opts || {}
var levels = opts.levels | 0 || 100;
var objDevider = opts.objDevider || '.';
var arrayDevider = opts.arrayDevider || [];
var showArrayAsObjec = !!opts.showArrayAsObjec;
var arrayDeviderPre = arrayDevider[0] || '[';
var arrayDeviderPost = arrayDevider[1] || ']';
function flatten(obj, path, result, level, parrentIsObj) {
if(level > levels){
return;
}
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var name
if(0===level){
name = prop
} else if (parrentIsObj || showArrayAsObjec){
name = path + objDevider + prop
}else{
name = path + arrayDeviderPre + prop + arrayDeviderPost
}
if (typeof obj[prop] == "object"){
flatten(obj[prop], name, result, level+1, obj[prop].constructor !== Array);
}else{
result[name] = obj[prop]
}
}
}
return result
}
return flatten(obj, '', {}, 0);
}
alasql.from.FLAT = function(obj, opts, cb, idx, query) {
var res = alasql.stdfn.FLAT(obj,opts);
if(cb){
res = cb(res, idx, query);
}
return res;
};
Let's create a separate issue for FLAT()
Отправлено с iPhone
14 янв. 2016 г., в 20:41, agershun@gmail.com написал(а):
For array we need to generate a separate line for each array element with all upper properties
Отправлено с iPhone
14 янв. 2016 г., в 20:40, agershun@gmail.com написал(а):
We were (I was)wrong. A little bit. I will describe it in an hour.
Отправлено с iPhone
14 янв. 2016 г., в 20:32, Mathias Rangel Wulff notifications@github.com написал(а):
Could you write a FROM function from the FLAT() function like:
I expect that 'filename' can be renamed to object and that the options are an object with settings
— Reply to this email directly or view it on GitHub.
@mathiasrw @agershun
Hi, Any update on the new the import/export/load/dump/store/restore/serialize/deserialize/ feature ? my team is using alasql and facing the same issue where the schema creating take a long time for 60 tables on iPad. we need an API to load/import the DB schema in one operation instead of creating the table one by one. I searched the new version of alasql 0.2.5, but could not find this APIs. Is there a plan in the near future to support the import/export? Thank you.
Hi @xiaqizhi
Glad to hear your find good use for this.
There is no plan on what to improve - only individual people contributing with what they like working with.
If you need this urgently you might want to contact @agershun and see if you can buy some of his time - or look into the matter yourself and contribute to the library.
@agershun, did some investigation to find the missing details between create table vs load schema using JSON. In below screen "window._memDB" is the one with createTable and "myContact" is the new one loaded from local storage (JSON).
Also, with schema loaded with JSON (local staorage), primary key doesn't work either. @agershun, need you help to resolve this issue, in case you are busy, please let me know probable fix area for loading schema through JSON so that we will try our best to fix, but given the complexity it may turn out to be difficult at our end.
Thanks for digging into this !
probable fix area for loading schema through JSON
All code interaction for the JSON storage file is located in https://github.com/agershun/alasql/blob/develop/src/94filestorage.js
@mathiasrw Thanks for your quick response. Currently we're trying to load the table schema from JSON that's stored in local storage. We have stored the property of columns, xcolumns, defaultfns, inddefs, uniqdefs and pk.
Code Sample:
var contactUniqdefs = JSON.stringify(window._memDB.tables.Sync.uniqdefs);
window.localStorage.setItem("contactUniqdefs", contactUniqdefs);
var myContact = new alasql.Table();
myContact.uniqdefs = JSON.parse(window.localStorage.getItem("contactUniqdefs"));
And then the issue occurs as @nkviru mentioned above.
We're wondering why some properties of UNIQUE and PRIMARY constraint are lost after such kind of conversion. Is there any API that can be invoked to set the UNIQUE or PRIMARY constraint?
Would you please kindly provide some fix for this issue or let us know the probable area to fix?
Any help/hint would be highly appreciated.
Hi,
I am using AlaSQL in my Ionic Capacitor app for web-based SQL operations on IndexedDB. The tables are created correctly, and data is inserted in the expected format initially. However, after refreshing the browser, the schema appears to be lost, and while data continues to be inserted, it no longer follows the correct format.
Is there a way to persist the schema, or am I overlooking something?
Here is how I’m initializing the database:
async initDB(): Promise<void> {
try {
const result = await alasql.promise(
`CREATE INDEXEDDB DATABASE IF NOT EXISTS ${this.dbName}`
);
await alasql.promise(`ATTACH INDEXEDDB DATABASE ${this.dbName}`);
await alasql.promise(`USE ${this.dbName}`);
console.log(`IndexedDB '${this.dbName}' initialized successfully`);
} catch (error) {
console.error('Error initializing IndexedDB:', error);
}
}
After this, I run the CREATE TABLE queries and then perform data insertions.
@Drashij Please open things like this on stack overflow or in https://github.com/AlaSQL/alasql/discussions
Below is the response from the AlaSQL bot
Your issue is likely due to not explicitly persisting the schema in IndexedDB after the initial page load. In AlaSQL, the schema (table definitions, indexes, etc.) is stored in memory, which is why it gets lost after a browser refresh. To resolve this, you should explicitly persist the schema in IndexedDB.
Here's how you can adjust your initialization function to ensure the schema persists across page loads:
async initDB(): Promise<void> {
try {
const dbExists = await alasql.promise(`SHOW TABLES FROM ${this.dbName}`);
if (dbExists.length === 0) {
// Create tables only if they don't exist
await alasql.promise(`
CREATE TABLE IF NOT EXISTS myTable (
id INT PRIMARY KEY,
name STRING
)
`);
// Additional CREATE TABLE queries go here
console.log(`Tables created in IndexedDB '${this.dbName}'`);
}
await alasql.promise(`ATTACH INDEXEDDB DATABASE ${this.dbName}`);
await alasql.promise(`USE ${this.dbName}`);
console.log(`IndexedDB '${this.dbName}' initialized successfully`);
} catch (error) {
console.error('Error initializing IndexedDB:', error);
}
}
SHOW TABLES
.With these adjustments, the schema should persist across page loads, and your data insertions should continue to follow the correct format.
Hi,
I am using Alasql to create bunch of tables, totally around 70. And I found it takes around 10 seconds to complete all of them on iPad Safari. Can you please kindly let us know whether this is expected or is there any way to improve the performance here?
Secondly, do we have any method to persist the schema aligned with the data from Alasql to IndexDB or WebSQL, so that we can create only once and reuse later?
Warm regards, Seal