Cross-Database Queries in SQLite
simonwillison.net
simonwillison.net
I expect to have time to make a demo datasette project available on replit (and github) 'soon.'
I'm so grateful for @simonw 's work on datasette and sqlite-utils! These packages are beautifully designed and documented, and they work so well! Thank you very much @simonw! EDIT: Of course, I am also grateful for sqlite, one of the best things ever written, not to mention this 'license:[3]'
# 2001 September 15 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give.
[0] https://datasette.io/ [1] https://sqlite-utils.datasette.io/en/stable/ [2] https://repl.it/ [3] https://sqlite.org/src/file/test/main.test
I mean the author didn't use transactions or anything so if there was any issue while reverting the user would end up losing data, but it's clever I guess. Not the best though. I'm rebuilding it and will likely be using versioning instead.
pipx install datasette
datasette sqlite.dbFor anyone who has Homebrew but doesn't use pipx, "brew install datasette" works too: https://docs.datasette.io/en/stable/installation.html
Marketing hated it, because it meant collecting user data took conscious effort which meant they had to ask the dev team, which meant requests were often turned down when hard questions were asked about whether it was justified to collate personal information.
That was a feature, from my perspective.
We actually took most user demographics data entirely offline. Data that was only intended to create aggregates anonymised profiles of our users were kept encrypted in bank box when not being analysed. Any analysis on it was done on an airgapped machine, and only the anonymised reports taken out.
The only central database that was online was one that kept a mapping of whether or not a given user name was available.
Then each storage shard kept track of which users were on which backend, and the account data that had to be online for each user (primarily their actual mail since we were a mail provider, along with settings etc. for their mailbox) were kept on a per user basis.
It worked well, but you need to have buyin from the top for this approach, as there will be constant pressure for easing access to more and more user data.
First, use advances in privacy technology to create a service-wide data warehouse that has enough information to help you make good decisions without exposing any specific user’s data. Done properly, users will benefit from your improved decision-making without giving up their personal data. Differential Privacy can do this.
Second, give users the opportunity to download their own little database in native format (e.g. SQLite) This is the ultimate in data portability. I think Dolt [0] might be good for this, because its git-like approach gives you push/pull syncing as well as diffing. That would make it easy for users to keep a local copy of the data up to date.
Third, you can start to support self-hosting and perhaps even open-source the primary user-facing application. The hosted service sells convenience and features enabled by the privacy-respecting data warehouse.
The big questions, of course, are many:
- Would users pay for this?
- Does increased development cost and reduced velocity outweigh the privacy benefits?
- Would the open-source component enable clones that undermine your business, or attract new users who may eventually upgrade to your paid service?
I would like to find out the answers!
I'm considering trying this with SQLite but I'm a bit worried about maintenance.
I was satisfied with our capabilities and enjoyed the peace of mind that it would be impossible for a tired developer (me) to write a user-facing query that could expose other customer data.
I am asking sincerely, what is the comparative advantage of sqlite or some example scenarios or trade off circumstances where sqlite is a comparatively more effective solution?
SQLite is used throughout Chrome and Firefox as a storage format, e.g. for browser history, bookmarks, cookies, etc. It's also commonly used as a storage format on macOS, iOS, and Android.
That's a pretty bold claim. Judging by the number of applications (including "real" databases) which fail to get this right, I'd have to say it's probably harder than you think.
Besides, SQLite has done that work already, and has done so very thoroughly. I would definitely trust SQLite over something home-grown.
- crashes/power failures
- multiple processes opening the same file
- transactional updates: it rolls back if you throw an exception in the middle of writing the update. This seems like a common failure mode while under development
- scaling to huge size: many gigabytes are no problem. These get slow with a flatfile
- manual surgery on data: it comes with handy command line tools for manipulating its databases
- upgrading format: you can check a version number when opening the file and do "alter table add column" for any new features.
Sqlite seems like it is only appropriate for the knife’s edge boundary between small data, low reliability, in-memory situations (better served by server applications using tools like pandas or R) and bigger data, transactional structure, reliability constraints (better served by Postgres).
I just can’t understand what use cases live in between them and are better served by sqlite.
It's honestly bizarre to me to see someone claim that Sqlite has few use cases when there are likely hundreds of Sqlite files sitting in the phone in your pocket.
Sometimes you do not want to run an additional process/daemon like postgres. Your state is in essence now a file on some file system that you can atomically (full ACID) update using multi processing without the need for more complex machinery - you can get very far with this architecture.
They’re not massive differences but i definitely appreciate the snappiness. Interactive work is just nicer that way.
I could do postgres - the docker version is pretty handy i find but it’s just more faffing about (-v /path/to/wherever:/var/postgres password, cleanup when you’re done etc.)
Sure beats regexing my way to the next minute marker or whatever.
Why would I use flatfile (aside from the fact I've never heard of it) over SQLite? So far you've just said SQLite has some features you can't imagine I'd ever need, but even if that were true then it wouldn't be a problem in itself. What are things that are actually bad about SQLite compared to the alternative you're suggesting?
Not sure why the sibling posters determined s/flatfile/flatfiles/ the most interesting part of your question.
There are advantages over SQLite, eg: the ability to use unix command line tools against the file directly.
In my view SQLite has more advantages though.
Of course principled command-line tools for text (e.g. awk) and the possibility of running arbitrary SQL queries on a database blur the line, but the distinction between files that are read or written once as a step in a transformation pipeline or as something to load or save and databases that represent a persistent state, with transactions, remains sharp.
Looking back, you seem to be right. I thought mlthoughts2018 was referring to a specific file format, or at least a specific library because (1) They made up that weird terminology "flatfile" which looks like product name instead of just saying "plain text file" ... I wonder why they did that? (2) They made claims about transactional integrity with a WAL-like log. Now I realise they were suggesting you homebrew that anew for each fresh project that needs to store data! The idea that this would be easier than just using SQLite's existing mechanisms is so bizarre it didn't even enter my head they could've meant that.
With SQLite you can have multiple processes access the same database and they can make changes to it that are immediately seen by the other processes. The nature of SQLite locks means this doesn't scale well if you have lots of processes all wanting to make heavy updates, but so long as you're not in that situation it works very well.
For example, they use it in the Photos app on macOS and in the Photos app on iOS. Their Core Data framework is an abstraction that is built on top of SQLite, and is available for application developers on iOS, macOS, etc.
Specifically about the Photos apps on macOS and iOS though, I was underwhelmed by the performance when you deal with 100,000+ pictures and videos. I don’t know if the sort of performance issues I was seeing was tied to SQLite or not though. My own solution to this has been to move away from using the Photos app on macOS all together, and to store my photos and videos in directory hierarchies instead. And to move photos and videos off of my phone as well, into the directory hierarchies on my hard drive.
It's certainly doable to serve million-asset libraries with SQLite (PhotoStructure is proof of this).
You need to be careful with indexed queries and make sure you've set up a large enough RAM cache, but for PhotoStructure, almost all queries are kept under 10-50ms.
For the record I dislike SQLite - especially the cargo culting of SQLite by people who aren't even using it in production.
But once, after a few years of perfect usage, the program crashed. Then it started crashing more regularly. Then finally it completely died. Data corrupted. Completely unusable. Major system malfunction.
I lost all my photos and videos in the Photos app.
But because I was always paranoid of it, I had the original pictures backed up elsewhere. So data saved. But I never trusted the Photos app again.
User registrations, content posts, in-app purchase receipts etc. Up to hundreds of thousands of concurrent users it works just fine.
It saves me a tone of time and complexity from setup I don’t have to do for a dedicated database engine.
There is a good overview with general use cases here: https://sqlite.org/whentouse.html
Yes this is a heavy query that would not make it to a production system, still I am surprised that load placed on a sqlite db by "hundreds of thousands of concurrent users" would not surface problems due to this simple detail.
https://sqlite.org/pragma.html#pragma_journal_mode
This lets sqlite read w/o a write lock (among other things)
Since it stores the log to an adjacent file you have to make sure the process can write to the whole directory containing the db.
Specially on how to deal with concurrent writes on SQLite.
With this approach, services are usually separated (e.g. microservices or a monolith with several db clients) and use several sqlite db files. For example, users.db, receipts.db, posts.db etc.
For data which is frequently accessed, it really helps to consider caching (e.g. HTTP).
I realise of course this won't scale infinitely so I usually make use of ORMs (e.g. GORM for Go/Gin or Diesel for Rust/Actix) just in case the SQL engine would need to change (so I don't have to rewrite queries etc). I haven't had the need to do so yet.
Data in a flat file would be much less efficient, and a SQL server (eg PostgreSQL) would be more for the administrator to deal with. SQLite's a nice sweet spot.
I've thought about using a key/value database (either a BTree one like SQLite uses under the hood or a LSM one like RocksDB). It'd be faster and less total code. But SQLite's too convenient to give up. The SQL interface is nice for debugging in particular. And it's already plenty fast enough, so my time's better spent adding some glaring missing features and better UI.
You use SQLite to back and operate an application. SQLite is a wonderfully lightweight transactional database; it compares more to e.g. MySQL than with OLAP systems like Spark.
SQLite isn't competing for analytical use cases :)
Managing it as separate csvs per customer allowed some incredible optimizations for fanning out processing and performing reporting and dashboarding. The process running pandas allowed us to do much nicer aggregations, pivots, filters, etc., and by not writing it in SQL, we had so much more flexibility in application code and especially in unit & integration testing code.
If data size per customer was going to grow substantially larger, we would have needed to migrate the workload to a backing SQL database, likely Postgres, but the nature of the problem meant this axis of data size was not a problem (every separate csv represented a completely isolated advertising campaign from a customer, with only up to a few million records per campaign).
Using a flask server program to do this in pandas was an aspect that really, really paid off for us.
But in many cases, yeah, simple file use is good enough, as long as important stuff is backed up somewhere / a human can easily re-upload and repair anything needed. It's a <0.1% optimization, it really only saves you noticeable effort when you're doing like millions of those operations per day.
- The parsing code for SQLite is exactly the same C code in every language, making it impossible to make mistakes in reading/writing.
- SQLite likely has stronger transactional writing ability than what your application created.
- Any SQL GUI will allow you to inspect and iterate on queries during development/production.
- SQL is a good first step to writing “join/filtering” queries and is backed by C code/indexes so should be fast for simple stuff.
Of course if you do not need any of those and are able to spend the extra time to write out your “queries” in pandas/Python that will work well too - just another way of doing the same thing.
Personally I like SQL as a first stop for prototyping, with the hope I do not need to use other tools. Joins, transactions and using the disk for state are all ”good enough” starting points, and I can take those techniques to any language I use.
Look through the licenses for the software included as part of your phone's OS. You'll find SQLite in there.
Search GitHub for sqlite, there are several projects with thousands of stars that use SQLite. Here's one: https://github.com/Tencent/wcdb
If I had to install a typical database or some search engine I would never have used it. It is more than enough for what I'm using it for.
As most people do, you are severely underestimating how much data sqlite can quickly work with. I doubt that any csv based solution could compete.
For some applications these properties make sense. I have hundreds of gigabytes of data that is only ever processed sequentially in a pipeline, I don't need random access -- for this use json/csv are fantastic.
Some examples [0][1], or just play with it yourself.
[0]: https://dphacks.com/2019/05/07/how-to-search-multiple-lightr... [1]: http://regex.info/blog/2006-07-29/221