After dealing with ActiveRecord for the past few years, I'm ready to just be able to write plain old SQL again. It seems like yesql is a pretty sweet solution, but I haven't tried it in earnest.
After dealing with ActiveRecord for the past few years, I'm ready to just be able to write plain old SQL again. It seems like yesql is a pretty sweet solution, but I haven't tried it in earnest.
On the other hand if you need dynamic queries, then yes dsl is more suited, as well as for other query types where the dsl can be much better at handling multiple records at once... like say inserting 10 rows at once.
So to sum up:
* complex select queries - templated queries really shine
* simple dynamic queries - templates for simple replacements
* complex dynamic queries - dsl
* inserts/updates/deletes - dsl
* portable sql - dsl
I work with lots of CTEs, UNIONS, JOINS, and sub-selects and can't imagine not using data structures to validate individual parts of the query as I'm designing it. Particularly given how trivial it is in HoneySQL (anywhere a sub-select is legal you can insert a HoneySQL query map).
Further, I use a lot of the jsonb and postgis features of Postgres and HoneySQL is a champ here. Not everything is supported out of the box[0], but adding any missing clause or operator is just a quick defmethod away. Between HoneySQL and clojure.java.jdbc, working with PostgreSQL's JSON support feels like Mongo with the added abilities of custom types, transactions, and function indexes.
I do have a limit with how far I'll take HoneySQL, but that limit is usually something that belongs in a trigger or a sproc anyway.
[0] honeysql-postgres (https://github.com/nilenso/honeysql-postgres) helps here by providing some postgres-specific clauses/operators.
As soon as I had to dynamically build queries (essential when moving beyond the complexity of "select this user record") I ran into issues with yesql's lack of power.
It also fits closer to one of the main principles of clojure, to be data-driven/data-first/just-use-data etc etc
This is definitely true, however, I'd like to point out that static queries (either Yesql-style, or even stored procedures) can go a lot further than you'd initially expect. Unless you have a pressing application need for dynamic queries, you may consider static queries as they are more predictable with respect to performance and debugging.
Here's the main trick: Most of the time, you want dynamic filters, not dynamic selections. When this happens, you can just include the total set of filters and provide default parameters that are always true.
For example:
SELECT id, name, created_at, score
FROM players
WHERE created_at BETWEEN '-infinity'::timestamp AND 'infinity'::timestamp
ORDER BY score * -1
This will return all players, regardless of when they joined and will sort by score descending. You can parameterize the time range as well as the sort direction between +1 and -1. You can use EXPLAIN to prove to yourself that Postgresql is smart enough to make proper use of indexes.In short, if your parameters are highly dynamic, plan caching will be disabled. This is still better than dynamic queries, because the results of parsing will be cached.
If you almost always use one particular set of filters, but just enough dynamic values that the cache plan is not useful, AND your bottleneck is planning, then you can prepare a fast path.
To do that, use a PREPARE statement or your language's SQL bindings for that. Essentially, you tell it "these are usually going to be the defaults, let's call that staticFoo". Then staticFoo and dynamicFoo are subject to independent plan caching. The fast path, staticFoo, will get a good cached query plan, and dynamicFoo will be subject to dynamic re-planning.
The braindead (and therefore "better", by some measure) approach is to just just create an additional query for your fast path. The tradeoff of course is violating the DRY principle.
https://github.com/bschwind/api-starter-kit/tree/master/apps...
EDIT: Actually I think the goal if I was to add more complex queries would be to use HugSQL for static, templated queries, and HoneySQL for highly dynamic queries that need conditions added or altered depending on input from other sources.