If you’re modeling relational data, it doesn’t seem like you can get around using a DB that uses SQL, which to me is the worst: most programmers aren’t DB experts and the SQL they output is quite often terrible.
Not to dunk on the lemmy devs, they do a good job, but they themselves know that their SQL is bad. Luckily there are community members who stepped up and are doing a great job at fixing the numerous performance issues and tuning the DB settings, but not everybody has that kind of support, nor time.
Also, the translation step from binary (program) -> text (SQL) -> binary (server), just feels quite wrong. For HTML and CSS, it’s fine, but for SQL, where injection is still in the top 10 security risks, is there something better?
Yes, there are ORMs, but some languages don’t have them (rust has diesel for example, which still requires you to write SQL) and it would be great to “just” have a DB with a binary protocol that makes it unnecessary to write an ORM.
Does such a thing exist? Is there something better than SQL out there?
No, SQL is great for relational data. There’s like decades of research into it. It’s the best.
relational databases have years of reseach into them, not the query language itself.
sql was built so people other than devs can use it, but we got stuck with it.
SQL has been around and evolving since the 70s. It is an ISO/IEC standard and is portable across a multitude of databases.
portable, my ass. excuse my french.
each system has it’s own dialect and quirks
each system has it’s own dialect and quirks
That does not mean that SQL, as specified by one of it’s standard versions, is not portable. It just means that some implementations fail to comply with the standard and/or provide their own extensions.
If an implementation fails to comply with the standard, that’s a failure on the side of the implementation, not a failure of SQL.
Could also be that the standard is lacking in some areas. I’m not sql expert but I always end up using implementation specific features even for rather simple tasks. Are there really people out there sticking 100% to standard SQL? Hell, the biggest implementations don’t even agree on whether table/column names are case sensitive
Could also be that the standard is lacking in some areas.
I don’t think that explains it.
If we’re talking about extensions that cover custom features then obviously those aren’t supposed to be standardized because they haven’t been widely adopted.
If an implementation is missing a feature then that’s a shortcoming of that particular implementation, not SQL’s.
If an implementation screws up and has non-compliance qwirks, that’s a bug in the implementation, not a problem with SQL.
Take SQLite for example. It explicitly does not support static, rigid typing, and claims it’s a feature. However, SQL supports static typing and other implementations leverage that for performance and cost gains. Additionally, SQLite also keeps a list with a summary of all the SQL features it purposely does not implement.
SQLite is pretty popular. Does this mean SQL is lacking in any way? Is the SQL standard “lacking” because it supports
ALTER TABLE foo ADD CONSTRAINT
even though SQLite does not? Or is this a problem caused by an implementation failing to comply with a standard?
sure, ok, but who uses only the subset of standard SQL in a particular engine just to call his queri3s portable? most of the good stuff is unique to each engine and is what makes the engine stand out.
it’s the same with C standards…
sql was built so people other than devs can use it, but we got stuck with it.
Not really. Being designed with UX in mind, so that it sacrifices conciseness for readability, does not make it something for “people other than devs”.
Likewise, BASIC was also developed with UX in mind, and no one in their right mind would ever claim that it’s not a programming language.
SQL is horrible as a language to read or write. There’s a million different variants, because it lacks so many basic things. And when used in other code, you generally end up string concatinating one language in another language, with all the HORRIBLE bugs something like that brings about.
Imagine Backend People said we should just write adhoc Javascript for the frontend by concatinating the “correct” code in the backend.
SQL, where injection is still in the top 10 security risks
This is absolutely true, but it’s not what it looks like on the surface, and if you dig into the OWASP entry for this, you’ll see they talk about mitigation.
You can completely eliminate the possibility of injection attacks using well-understood technologies such as bind variables, which an ORM will usually use under the covers but which you can also use with your own queries. There are many, many database applications that have never once had a SQL injection vulnerability and never will.
The reason SQL injection is a widespread security risk, to be blunt, is that there are astonishingly large numbers of inexperienced and/or low-skill developers out there who haven’t learned how to use the tools at their disposal. The techniques for avoiding injection vulnerability are simple and have been well-documented for literally decades but they can’t help if a lousy dev decides to ignore them.
Now, a case could be made that it’d be better if instead, we were using a query language (maybe even a variant of SQL) that made injection attacks impossible. I agree in principle, but (a) I think this ends up being a lot harder than it looks if you want to maintain the same expressive power and flexibility SQL has, (b) given that SQL exists, “get bad devs to stop using SQL” doesn’t seem any more likely to succeed than “get bad devs to use bind variables,” and © I have too much faith in the ability of devs to introduce security vulnerabilities against all odds.
It’s also worth noting that the current entry on OWASP is injection generically. It includes SQL, but it also covers things like HTTP links where you concatenate in unchecked user input. SQL injection by itself may no longer be prominent enough make the list.
I’m also going to put the blame squarely on PHP for SQL injection attacks hanging on for so long, particularly when combined with MySQL. That DB didn’t support bind variables for a long time (maybe still doesn’t?). Other languages may have used a library that simulated bind variables for you. Barring that, they tended to always always always show how to use a sanitation function even if it was the first mention in the first tutorial for the top Google result for “sql [language]”. That creates a culture in the language of writing safe SQL code. Not PHP, though; the sanitation functions were there, but they never gave them the prominence that they so badly needed.
I’m absolutely biased as a data engineer who loves SQL, but there are some good reasons why SQL has been the de facto standard for interacting with databases since the 80s.
One of its draws is that it’s easy to understand. I can show a stakeholder that I’m selecting “sum(sale_amount) from transactions where date=yesterday” and they understand it. Many analysts are even able to write complicated queries when they don’t know anything else about programming.
Since it’s declarative, you rarely have to think about all the underlying fuckery that lets you query something like terabytes of data in redshift in minutes.
Debugging is often pretty nice too. I can take some query that didn’t do what it was supposed to and run it over and over in a console until the output is right.
I’m absolutely biased as a data engineer who loves SQL, but there are some good reasons why SQL has been the de facto standard for interacting with databases since the 80s.
I find it funny how the people who actually have to wrangle data swear by SQL as awesome, but there are always random hacks coming out of the woodwork, who don’t even look at SQL at all, with sweeping statements claiming SQL sucks because reasons.
It’s like the most opinionated people against SQL are the ones who don’t use SQL.
it’s just a drag to veer from. it’s not particularly good, but good enough to stick around.
deleted by creator
“If you don’t know what I know, you shouldn’t be here”
“I don’t make mistakes that end up #3 on the vulnerability list, and if you do, I don’t want you around me”
I find that a gate keeperish attitude.
deleted by creator
Gates exist for a reason
And yet, it still happens all the time.
I have found that usually the problem isn’t SQL itself, it’s the design of the database or the application or both that makes it slow or hard to use because databases are tricky and it’s very easy to make them slow and difficult to optimize. This goes for any database, SQL or otherwise.
I think the reason that you see so many people use relational databases for relational data is that they were designed for it and have been optimized to do that job for almost 50 years at this point, the right tool for the job if you will. That’s also why I don’t think any language builds it in, there isn’t one type of database that is good at every scenario so it would be difficult to pick a default as a language. Also keep in mind, most large applications are deployed across many servers so anything that uses a local file is out of the question.
As far as performance directly though, I’m definitely not a DBA but I have spent a lot of time helping people troubleshoot slow databases and it seems many people write apps and design databases based only on how they want to store the data and not how the actual data is consumed. The other thing I think most folks don’t seem to realize (including myself for a long time) know the need for creating good indexes based on how you are accessing the data. It’s not an easy problem to solve for sure though.
Database optimization goes from the design of the schemas to the queries used to the configuration to the actual hardware it’s running on. It’s one of the hardest things to optimize in all of tech.
I am both a (T-)SQL expert and a language design enthusiast. IMO, SQL the language is mediocre in its grammar and extremely resistant to cleanliness. Once you get past that, the things you can actually do with it are extremely useful.
I’d love for a better syntax to exist, but it’s a Herculean task to make one. Modern SQL dialects have gargantuan, labyrinthine grammars, and they grow with each new product version. It’s a lot easier to keep adding to that than to build a feature-complete replacement. This is also the reason why most ORMs are so frustratingly limiting: it’s too much work to support the advanced features of one SQL dialect, let alone multiple.
most programmers aren’t DB experts and the SQL they output is quite often terrible.
Isn’t that looking at it the wrong way / missing the point?
If you’re fine with simple queries, use an ORM of your tech stack. Once you have to understand querying performance and weigh usage patterns, constraints, and cache performance and size, then it’s not about SQL anymore. It’s about understanding the DBMS you use.
You may ask “why can’t I use a different language for the querying part of it”. But I haven’t seen anything better yet.
Having a common, well-understood, established, documented, inter-product compatible language has a lot of value. Using a different language to the well-established industry standard is in itself an increased hurdle to adoption.
Getting back to the original quote: I don’t think anything else would serve bad developers / non-experts any better.
If you’re fine with simple queries, use an ORM of your tech stack. Once you have to understand querying performance and weigh usage patterns, constraints, and cache performance and size, then it’s not about SQL anymore. It’s about understanding the DBMS you use.
Simple queries don’t result in simple SQL. How many joins and subqueries do you think an SQL query would require in order fulfill “Give me the top 10 artists of the 90s whose albums were nominated for the MTV awards but didn’t win”?
In Django looks something like
nineties = (date(1,1,1990), date(31, 12, 1999) album_range=Q(albums__release_date__range=nineties) artists = Artists.objects.annotate( albums_sold=Sum("albums__sales", filter=album_range)), ).filter( album_range, nominations__date__range=nineties, nominations__won=False ).order_by("-albums_sold") top_artists = artists[:10]
What if one method wants the result of that but only wants the artists’ names, but another one wanted additional or other fields? In django you could simply use
artists.only(*field_names)
and each method would provide a different set of field names. What would that look like without a capable ORM? Do you think somebody would refactor the method to add afield_names
argument? In my experience the result is a bunch of copy pasted queries that modify the query itself to add the fieldnames.Another common thing is querying related objects. Say you simply wanted to have information about the record label of the aforementioned artists while handling the artists. A many-to-one relationship (artist has one record label, record label has many artists). You could either
artist.record_label
while in your for-loop, but that would trigger an query for every artist (1+n problem). Or in django that’sartists.select_related("record_label")
and it will get all the record_labels in the same query.
If it’s a many-to-many relationship for example “festivals”, then.prefetch_related()
will first select the artists, then make a second query of festivals of those artists, andartist.festivals
would be available.An ORM like django makes that simple. SQL, does not.
So, before we even get to the DB optimisation part (which indices to create, whether a view is better or now, which storage engine to use, WAL size, yadayadayada), there’s an entire interface / language that makes writing bad code very easy.
I’m too lazy to convert that by hand, but here’s what chatgpt converted that to for SQL, for the sake of discussion:
SELECT a.id, a.artist_name -- or whatever the name column is in the 'artists' table FROM artists a JOIN albums al ON a.id = al.artist_id JOIN nominations n ON al.id = n.album_id -- assuming nominations are for albums WHERE al.release_date BETWEEN '1990-01-01' AND '1999-12-31' AND n.award = 'MTV' -- assuming there's a column that specifies the award name AND n.won = FALSE GROUP BY a.id, a.artist_name -- or whatever the name column is in the 'artists' table ORDER BY COUNT(DISTINCT n.id) DESC, a.artist_name -- ordering by the number of nominations, then by artist name LIMIT 10;
I like Django’s ORM just fine, but that SQL isn’t too bad (it’s also slightly different than your version though, but works fine as an example). I also like PyPika sometimes for building queries when I’m not using Django or SQLAlchemy, and here’s that version:
q = ( Query .from_(artists) .join(albums).on(artists.id == albums.artist_id) .join(nominations).on(albums.id == nominations.album_id) .select(artists.id, artists.artist_name) # assuming the column is named artist_name .where(albums.release_date.between('1990-01-01', '1999-12-31')) .where(nominations.award == 'MTV') .where(nominations.won == False) .groupby(artists.id, artists.artist_name) .orderby(fn.Count(nominations.id).desc(), artists.artist_name) .limit(10) )
I think PyPika answers your concerns about
What if one method wants the result of that but only wants the artists’ names, but another one wanted additional or other fields?
It’s just regular Python code, same as the Django ORM.
it would be great to “just” have a DB with a binary protocol that makes it unnecessary to write an ORM.
Other people have talked about other parts of the post so I want to focus on this one.
The problem an ORM solves is not a problem of SQL being textual. Just switching to a binary representation will have little or no impact on the need for an ORM. The ORM is solving the problem that’s in its name: bridging the conceptual gap between an object-oriented data model and a relational data model. “A relational data model” isn’t about how queries are represented in a wire protocol; instead, it is about how data, and relationships between pieces of data, are organized.
So, okay, what if you get rid of the relational data model and make your database store objects directly? You can! NoSQL databases had a surge in popularity not too long ago, and before that, there have been lots of object databases.
What you’re likely to discover in an application of any real complexity, though, and the reason the industry has cooled somewhat on NoSQL databases after the initial hype cycle, is that the relational model turns out to be popular for a reason: it is extremely useful, and some of its useful properties are awkward to express in terms of operations on objects. True, you can ditch the ORM, but often you end up introducing complex queries to do things that are simple in SQL and the net result is more complex and harder to maintain than when you started. (Note “often” here; sometimes non-relational databases are the best tool for the job.)
And even in an object database, you still have to know what you’re doing! Storing objects instead of relational tuples won’t magically cause all your previously-slow queries to become lightning-fast. You will still need to think about data access patterns and indexes and caching and the rest. If the problem you’re trying to solve is “my queries are inefficient,” fixing the queries is a much better first step than ditching the entire database and starting over.
Even some of these issues ORMs are solving can be solved without them by caching a view of the data in structure of the object. Relational DBs are extremely well tuned for looking up and caching data in an easy to view manner.
Somebody else pointed out the problem is bad devs not learning their tools. I’d go so far as to say DB knowledge can (and was due a while) be a specialized field full of skills that will fall by the wayside for most devs because we aren’t doing super complex things in a single DB anymore with the preference going toward microservices. There’s no need to flex those skills and they depreciate over time.
the translation step from binary (program) -> text (SQL) -> binary (server)
Your concern about this is misguided. Inter-process communication always has to cross a barrier, by definition.
I take it http also feels wrong to you?
Binary protocols do exist, e.g. gRPC, but they incur costs of their own.
the translation step from binary (program) -> text (SQL) -> binary (server)
Also, the translation step from binary (program) -> text (SQL) -> binary (server), just feels quite wrong. For HTML and CSS, it’s fine, but for SQL, where injection is still in the top 10 security risks, is there something better?
Does this not answer your question?
I don’t know if there is, but it feels like the email protocol problem.
Like, while the protocol sucks in many, many ways, it would take something revolutionary to replace it because it’s everywhere.
It’s been around so long that everything talks the protocol, the binaries that handle it are mature and stable.
Then you have to ask: what would you replace it with? It does the job it’s designed to do very well. There’s nothing the matter with the protocol, and it’s still fit-for-purpose.
That doesn’t mean there aren’t problems - spam, bad actors, and so on, but ultimately that’s not the fault of the protocol (though, maybe, for email, people have been arguing about protocol-level ways of dealing with spam for years).
I don’t have an answer, but I feel like there should be one, but I doubt the is.
(have heard a similar argument about Python – there’s no killer app and it’s not the best, but it’s good enough and just keeps going)
Then you have to ask: what would you replace it with? It does the job it’s designed to do very well. There’s nothing the matter with the protocol, and it’s still fit-for-purpose.
Rust once it’s matured is my guess
Edit: this short comment wasn’t thought out so the downvotes are reasonable, gonna leave this up so I can improve
We’re replacing protocols with Rust now?
I imagine they were trying to reply to CEREMENT about Python.
ElasticSearch tried. It’s without a doubt one of the worst query languages to use. Nobody can remember how to write the queries, or even reading the aggregations. You literally need the docs open on another monitor.
A solid No from me. But I also never got the ORM hate, ActiveRecord on Ruby made it devine imo, sequelize on JS is fine, Diesel in rust is good
ORM with an easy and straightforward way to execute custom queries is the sweet spot to me. Let the ORM handle the basics (and migrations), override / add optimized queries where relevant.
SQL is the industry standard for a reason, it’s well known and it does the job quite well. The important part of any technology is to use it when it’s advantageous, not to use it for everything. SQL works great for looking up relational data, but isn’t a replacement for a filesystem. I’ll try to address each concern separately, and this is only my opinion and not some consensus:
Most programmers aren’t DB experts: Most programmers aren’t “experts”, period, so we need to work with this. IT is a wide and varied field that requires a vast depth of knowledge in specific domains to be an “expert” in just that domain. This is why teams break up responsibilities, the fact the community came in and fixed the issues doesn’t change the fact the program did work before. This is all normal in development, you get things working in an acceptable manner and when the requirements change (in the lemmy example, this would be scaling requirements) you fix those problems.
translation step from binary (program): If you are using SQL to store binary data, this might cause performance issues. SQL isn’t an all in one data store, it’s a database for running queries against relational data. I would say this is an architecture problem, as there are better methods for storing and distributing binary blobs of data. If you are talking about parsing strings, string parsing is probably one of the least demanding parts of a SQL query. Prepared statements can also be used to separate the query logic from the data and alleviate the SQL injection attack vector.
Yes, there are ORMs: And you’ll see a ton of developers despise ORMs. They is an additional layer of abstraction that can either help or hinder depending on the application. Sure, they make things real easy but they can also cause many of the problems you are mentioning, like performance bottlenecks. Query builders can also be used to create SQL queries in a manner similar to an ORM if writing plain string-based queries isn’t ideal.
I used to be full on the ORM train. Now I’m a little less enthusiastic. What I actually think people need most of the time is something closer to ActiveRecord. Something that can easily map a result set into a collection of typed objects. You still generally write parameterized SQL, but the work of translating a db decimal into the correct target type on a record object in your language is handled for you (for example). In .net, Dapper is a good example.
I also think most people overemphasize or talk about how other programmers “suck at SQL” waaayy too much.
IMO, for most situations, these are the few high-level things that devs should be vigilant about:
- parameterize all sql.
- consider the big-o of the app-side lookup/write methods (sometimes an app join or pulling a larger set and filtering in memory is better than crafting very complex projections in sql). This is a little harder to analyze with an ORM, but not by much if you keep the mappings simple and understand the loading semantics of the ORM.
- understand the index coverage of queries and model table keys properly to maintain insert performance (monotonically increasing keys).
- stop fixating on optimizing queries that run in a few seconds, a few times a day. Optimize the stuff that you run on every transaction - if you need to.
On most of those points, if you don’t have aggregate query counts/metrics on query performance on your clusters, starting to get cute with complex queries is flying blind, and there’s no way to prioritize what to optimize.
For the vast majority of cases, simple, obvious selects that don’t involve special db features are going to do the job for most applications. When the database becomes a bottleneck, there are usually much more effective ways to handle them than to try to hand optimize all the queries.
Lastly, I have a little bit of a theory that part of the reason people do/do not like looking at SQL in code is because it’s a hard context switch from one language to another, often requiring the programmer to switch to “stringly-typed” mode, something we all learn causes huge numbers of headaches in our first few months of programming. Some developers accept that there’s going to be different languages/contexts and not all of them are going to be as fluent or familiar, but accept that this is par for the job. Others recoil from the unfamiliar and want to burn it down. IMO, the former attitude is a lot more productive.
Without a DSL for writing SQL, any sufficiently complex program will end up with string concatinating all over the place. Basically, writing a language with ZERO checks or highlighting or anything. That’s asking for trouble.
But coming from Java, I agree that some ORMs go way too far.
It’s necessarily complexity that is easily encapsulated in methods.
If those methods are under test to verify their behavior, trivial typos can be detected instantly, without adding another dialect and more conceptual overhead to a project.
If those methods are not under test, then there’s a tiny bit of help by using a DSL if it can be compile-time checked.
SQL is a great tool to have in your toolbox. From creating adhoc reports to building any kind of backend, it always seems to come in handy.
There are obviously non relational databases available and other approaches to persisting information, but there are some desirable characteristics of traditional relational databases.
Oftentimes I find if I’m struggling it is because of a missing abstraction. Views, functions, etc. are available to extract higher level ideas and remove duplication.
I think you missed the point. OP is asking for an alternative to communicate with a relational DB.
Yes, exactly. You got it.
One alternative to both raw SQL and and ORM is a query builder, a procedural library for constructing database queries. Query builders typically don’t have the object/relational “impedance mismatch” of ORMs; they don’t encourage you to pretend that records in the database are the same as objects in your code. But they give you a syntax that looks more like your programming language, and automatically handles escaping (and thus, resistance to injection attacks).
However, query builders often don’t expose all the power of your database. If you’re using PostgreSQL, you’ve got one hell of a powerful set of tools in there. It’s often worth spending the time to master them just so you don’t end up reinventing the stone-age wheel on top of a warp-speed hovercraft.
I’ve been using Jooq to build my queries (and run them). Beats the hell out of writing prepared statements in strings.
Not sure what power I’m missing though, I’ve been able to do everything via Jooq that I want to do.
You’re not missing much power with jOOQ, in my opinion as someone who has used it for years. Its built-in coverage of the SQL syntax of all the major database engines is quite good, and it has easy type-safe escape hatches if you need to express something it doesn’t support natively.