SQL queries don't start with SELECT
jvns.ca
jvns.ca
Even sooner than "start with FROM" is "What does each row of the result mean?" Is it a sale? A month of sales? A customer? If you can answer what you want, then it's usually easy to start there. Especially if your answer corresponds to an existing table, put that in the FROM, and then add whatever joins you need. You can even think about the joins "imperatively".
Thinking imperatively about JOINs is also a helpful way of understanding a mistake I call "too many outer joins", which I wrote about here: https://illuminatedcomputing.com/posts/2015/02/too_many_oute... If you don't know about this mistake, it's easy to get the wrong answer from aggregate functions like AVG or COUNT. (EDIT: I should probably call this "too many joins" instead, since it's not really specific to outer joins. But in practice I find outer joins are more often used in these sort of situations.)
If you type just a table name into an empty cell it will autocomplete with a full SELECT statement. e.g. if you typed "pubuser" and selected "public.users" it would autocomplete with:
SELECT pu.*
FROM public.users AS pu
WHERE 1=1
Disclaimer: I built this.This is absolutely right. In fact, my ideal query language would require the programmer to be explicit if they want to increase the cardinality of the result set. Meaning, if I start a query with "from Employee ..." the query language should not let me accidentally add a join that will cause the result set to contain the same Employee more than once. (It should be possible, but only if I make it clear that it is intentional and not an accident.)
DELETE FROM
Customers WHERE
Name='Gude' AND
Area='Tama'
;
Much harder to leave off half the clause doing this kind of thing.It doesn't save me time, but a lot of nerves.
SELECT *
-- DELETE
FROM Sales
WHERE Customer = 1
Next, selecting the whole query and executing it. If results are satisfactory - selecting only the DELETE part. select * -- delete
from sales
where customer = 1
so it's even harder to accidentally highlight the 'delete' part.Although I've switched to DBeaver for a year now, and it automatically pops up a warning when it detects a DELETE query without a WHERE, which is very nice.
Everything is non-destructive without an explicit COMMIT.
Then if you make an error you can always ROLLBACK. If it looks OK -- COMMIT.
Similarly `git push ... -f` etc.
--safe-updates
to avoid queries running without WHERE clause.
Yep. As developers/designers we need to understand this as much as we wished our clients did.
Interesting because LINQ starts the same way, or at least that's how I remember it.
Seriously, between navigational properties, group-with-children, EF to me hammers home how bad SQL is. EF is a square peg in a round hole but it does a great job at makign some trivial improvements over SQL, while still remaining true to the principles of relational databases.
Of course, the framework is a hairy mess for other reasons (lazy loading, untranslateable methods, ugly generated queries) but the alternative to SQL it presents is lovely.
There are a bunch of tech that's tried to reinvent the wheel with SQL like Redis and Splunk... both of those systems are just a pain to interact with - their variants have strengths, sure, but they lose the ease of declaration and universality of SQL. I'd rather see a SQL variant emerge (if it were to) as a logically compatible declarative language that various DB vendors can add support for.
So if it results in same AST, the parser should be flexible. Someone should make PRs for mysql, slqlite and Postgres.
If those three have it, other engines will catch up real fast.
EF is just an ORM that benefits from using the LINQ syntax and operators available in .NET/C#.
Yes, EF queries are more readable to many developers who know C# well but don't want to get used to SQL. But the problem with that is DB apathy + EF leads to some real performance problems as systems scale that grinds the application to a halt with SQL queries longer than dissertations, to which the developers need a quick hack to fix. The query plan for those beasts is intractable. Which of the 10 table scans or index scans to tackle first? And how do you tackle it?
They discover "indexed views" and all is well until you start to have locking problems, so more hacks are needed. etc. By writing the damn SQL to begin with they'd have to tame the beast earlier on and address the design flaws in their schema and/or application.
Ok this is a bit of a strawman (although it is based on experience) but I think saying SQL is less readable is a red herring. It is more readable for a lot of use cases. It's a DSL designed for the job, you really can't get much better (except for Elastic Search use cases, etc.). It's slightly old fashioned but that's just fashion.
SQL is far from perfect. It would be nice to see some modern SQL dialects that follow the LINQ syntax and are natively supported by RDBMS.
"from c in customers select ..."
Fill in the blank. They're not equally readable to the IDE that's trying to provide $1,199 worth of code completion for a partially typed query.
My main field is databases. Been working with SQL for ~15 years. My knowledge of LINQ is much less than SQL (partly also because I was writing plenty of SQL before LINQ existed). I also think SQL is a big big mess full of leaky abstractions that forces you to do things in unituitive ways. And yet it is incredibly powerful. But don't listen to me. Read any book by C.J.Date (especially this one http://www.thethirdmanifesto.com/), for all the reasons SQL is poorly designed.
> "But the problem with that is DB apathy.....leads to some real performance problems....."
I completely agree. Databases are very powerful and more people should know how to properly use them so that the rest of us would have fewer headaches.
> " It's a DSL designed for the job, you really can't get much better"
The problem is that due to many reasons it is the only such DSL in wide spread use. Any alternatives are just footnotes.
When we solve problems using relations, we benefit by thinking in terms of relations.
We can ask: How well does SQL allow us to express an algorithm in relational terms? (Let's ignore that that SQL introduces some non-relational impurities that may have practical value - eg column order, row order, duplicate rows.)
When I examine an SQL expression, and then visualize the relational operators involved, I find a very poor correspondence between the SQL expression and the relational algebra. I need to basically read the entire statement to understand the parts. (Contast to the LINQ examples, or perhaps a shell pipline.) In a way, this is the substance of the original article.
This is one way in which I mean SQL is bad.
It gets it "right" (right being a very loosely defined term here) by happenstance, because you're operating off of an object. Not because it was some higher level design choice or discussion.
What is wrong with lazy loading enabled by default?
Ideally, you should know ahead of time what data you need and how to pre-load it without relying on something that could cause multiple N+1 issues based on the depth of lazy loading.
It's my primary tool for querying against databases, because like you, I really prefer writing LINQ queries to SQL. It won't let you do most DB admin tasks, but that's what SSMS is for.
I'm soooo gonna miss that when we have to add MSSQL support soonish. Having to repeat myself is tedious, especial for non-trivial expressions.
Are CTEs an optimization barrier in MSSQL? I would've thought that the query planner could move the execution plan nodes across the CTEs.
They are not. They are, however, in Postgres.
edit: I say this from experience fixing hundreds of CTEs that came down to "I want to simplify the downstream query so I am going to package a heck ton of relations in this cte and combine it with this other one to hide the complexity as I do some other stuff!"
We do a lot of queries like
select x.a, (select count(y.b) from tableY y where y.id = x.id) as cnt
from tableX x where x.ref = :ref
where cnt > 0
Typically a lot more going on than this, but this gets the point across I think. Would we have to drop for each select then?If you wanted the CTE/temp table to re-evaluate the query for each select, yeah, you'd effectively have to drop/truncate - but my read on your code is that the subquery would turn into a temp table, be re-used across all your selects, then go out of scope, and disappear.
I'm really glad the accompanying article has a bunch of qualifications of the form "Database engines don’t actually literally run queries in this order" but a lot of the beauty of these diagrams is that they work on their own.
I wish the title of the picture said something more like "SQL queries are interpreted in this order" to make it clear we're talking about semantics and not what actually takes place.
If your code may run from 3m to 3 hours, its probably the SQL at fault.
In my experience, you have one of three scenarios:
You know ahead of time exactly the problem you are going to solve and so probably shouldnt choose SQL from a performance perspective.
Your problem size changes and you get to pick a simple system that you understand well and has predictable performance but might be slower than other options.
Or a complex system that's less well understood, and generally has great performance and productivity up to a tipping point, where the abstraction has leaked too much.
In your case - your query sounds exactly like those heuristics worked up to a point, and then finally failed. Is that the optimizers "fault"? It's mostly a trade off between the time spent making the plan and the time spent getting your data back - and again, sometimes those tradeoffs are wrong too.
(This is assuming whatever applicable regular maintenance (stats, index or table reorgs if needed) is done prior to the time-sensitive batch... and understanding sometimes there's just not enough hours in a day to run batch AND maintenance :| )
SQL and other declarative languages can be frustrating to talk about because what you tell it you want and _how_ it does the work isn't supposed to matter. So there are two questions that you might answer with either jvsn's answer (how should you imagine it works to understand how to use SQL?) and your answer (how should you imagine it works to figure out why it's slow?). Someone learning SQL is probably asking the first question, somebody debugging performance is probably asking the second one.
So this is a weird criticism because neither of you is incorrect, it's just that different audiences require different kinds of detail. If she instead wrote the answer to your question then someone else would inevitably come along with a comment nearly identical to yours except it would say "well that's just implementation details but you should instead imagine that...".
Yep! Article is a big improvement; first I saw the image was earlier today when it was tweeted on its own here: https://twitter.com/b0rk/status/1179449535938076673?s=20 and her images are often awesome because they capture nuance in a tiny poster
is it only my (wrong) subjective feeling, or did the one of Oracle become much more stubborn about following "hints"? https://en.wikipedia.org/wiki/Hint_(SQL)
Reason: I started dealing with Oracle when it was at v8 and I think up to including v9 when I specified a hint it was usually followed. Then later with v10 and v11 it progressively became more difficult and nowadays with v12 I'm having a really hard time (I usually have to use some "tricks" that don't change the logic of the SQL but do change its technical execution, like placing somewhere a useless but tactical "distinct", to confuse it enough so that my hints are more or less followed/used).
Btw., if you want to state something like "using hints is wrong" then in general I agree (with some exceptions for special cases) but currently I'm taking care of a very old app that uses often quite big SQLs (involving up to ~15 tables) and as the maintenance budget is limited and the app will anyway be decommissioned in 1-2 years I cannot start rewriting half of the application to break down and improve the statements => when once per quarter the data distribution/quantity/etc... change and the optimizer thinks that it has a new brilliant idea about how to exec some SQL and then of course the SQL hangs then I usually just try to spend 1-2 hours trying to find some hint(s) that will bring back the old execution plan, but since we upgraded to Oracle12 I often see no change in the exec plan unless I do what I mentioned above.
At the company we're using EE, and I've heard about the plan locking functionality, but I never dared to use it.
Does it survive DB-restarts? Additionally we have a setup of an active/primary cluster that is replicated to a passive/secondary one in our secondary datacenter (which then becomes leading in case of a disaster in the primary datacenter) => I don't think that a locked plan is replicated to the secondary cluster (which, in a case of a disaster would become a 2nd disaster as many SQL all of a sudden would stop working).
But thanks for the hint :)
This is a very common truth, I've been in similar situations where budgets are tight and things are working so don't touch a thing. I think it's actually just a specific example of a very common problem pattern in tech - the way I usually push back on it is "The stuff that is is and I'm not going to waste time fixing it - but any stuff I'm adding a feature to or fixing a bug in, it's not worth the company's time to fix it the wrong way because it'll end up costing more when another bug or feature requires an adjustment near this in a year." All that said, especially within MySQL (and I haven't worked in Oracle so maybe there too) the query planner is a bit dumb, so sometime you really need to help it along.
Yeah, I've seen it as well last year when working with MariaDB (but I'd guess that MySQL might be a bit more clever as maybe Oracle might have improved it a bit).
For some reason that made sense to me.
WITH
employee AS (SELECT * FROM EMPLOYEES),
department AS (SELECT * FROM DEPARTMENTS)
SELECT employee.name, department.name
FROM employee
INNER JOIN department ON employee.department_id = department.department_idThis works with IntelliJ and Datagrip, I don't know about other IDEs.
From a source code structure perspective "SELECT foo ...;" sort of works like a function header: "function foo() { ... };"
- 1. create a bunch of constructs to work towards that goal -
- 2. work happens -
- 3. finally get my output and have what i want -
OTOH FROM first is better for tooling.
Really, there's no really good reason for a mandatory order of clauses in an SQL statement.
The thing I always imagine is a bunch of people in the 1970ies sitting around designing this stuff, and reading it out loud in a Captain Kirk voice
"Computer, SELECT course WHERE Klingons IS zero!"
1. "SELECT *" because habits
2. Start writing the FROM/JOINs and table aliases
3. Go back and refine the SELECT to tableAlias.ColumnName
4. (OPTIONAL) Aggregates
5. Forget the WHERE clause and burn the optimizer's cached query plans to the ground! (j/k, add WHERE or just throw a TOP 500 into the SELECT)
It’s similar to how I write Python list comprehensions. I almost always type “x for x in” from muscle memory, then get the collection part at the end correct, then go back and fix the beginning. My brain just won’t think in the other direction.
Compare scala's for-comprehension that feels more natural: `for (x <- xs) yield x + 1;`
Perhaps there's an argument that the shape of the result is defined up front, so it might be easier to comprehend the result if not the implementation.
If you type just a table name into an empty cell it will autocomplete with a full SELECT statement. e.g. if you typed "pubuser" and selected "public.users" it would autocomplete with:
SELECT pu.*
FROM public.users AS pu
WHERE 1=1
It will even take a stab at aliasing the table for you.Disclaimer: I built this.
I don't remember the specifics, just that the acronym was something like "flower" and you started with your from clause and ended with what you would return.
It leaves out SQL window functions though.
There's a bonus in doing that - you get auto completion from the editors on many cases if you've filled the from first.
https://www.loom.com/share/9c7979a163eb4513b3320e6e90c66079
If you type just a table name into an empty cell it will autocomplete with a full SELECT statement. e.g. if you typed "pubuser" and selected "public.users" it would autocomplete with:
SELECT pu.*
FROM public.users AS pu
WHERE 1=1
Disclaimer: I built this.I wish she did a monad one, you bet it wouldn't be "it's just a monoid in the category of endofunctors, what's the problem".
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_...
'SELECT columns FROM table' makes sense when you're asking it literally. However SQL and relational DB's have evolved since then and the English grammar is now awkward. It would be nice to see newer dialects accept the inverse order to help with tooling and structure.
And a very practical advantage is that the editor can support syntax completion. SQL syntax cannot support that since you write the projection before the from-clause.
See https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...
I absolutely hate the SQL-ish LINQ expressions in C# code. It's really awkward when you make a query and need to materialize it. You either store the Queryable<> in a temporary variable and then convert it to a list or an array or whatever, or you have to glom parentheses around a big LINQ expression.
CTEs seems like a very nice middle ground for code reuse - enshrining them in views is nice for somethings but keeping them raw and composable is to my preference since application developers aren't losing any visibility of logic occurring within the composed in blocks... this is definitely not a simple problem to solve though and is a constant balancing act so there is some clear room for improvement.
I want a null-handling-equals macro, for example (god I hate three-value-logic so much).
At my current work nobody talks about SQL, literally every discussion starts with "We will create this new Rails model". Indexes are not created deliberately - we are adding them only when the need is obvious, because some functionality is plain slow. No one talks about SQL, plain SQL queries in code are considered really bad practice - in cases where the query generated by Rails is complex and slow and a plain SQL query is PR-ed, the code reviews discussions are pretty long.
I am not saying this is good or bad, in fact all looks OK, but I was really really surprised at the beginning.
Because you're leaving an unmaintainable mess. Nobody else can rationalise the code aside himself. It's harder to debug because even he cannot account for every edge case his regex might throw up -- which also makes it less secure. Also regex is comparatively slow so transforming an SQL query using multiple regex search and replace patterns is not the way to write hot code hit by literally millions of visitors each hour.
> understanding his code generator is not (necessarily) a pre-requisite for understanding the resulting code.
Oh it absolutely is if you want maintainable and secure code. I get some situations call for complexity but when you're generating SQL on a public facing web application with a centralised database backend, you want to be damn sure you can rationalise the SQL being generated. The best ways of doing that is to either not to try to be too clever with your generation (KISS) or to put your confidence into an established and well tested existing ORM or equivalent.
Is there anything wrong with this? My thinking is that it could be seen as a premature optimization if an index is created without knowing if it will be used.
I think that the fast majority of movement away from relational databases is pure folly. With JSON columns and horizontal scaling, relational DBs can handle virtually every workload, and give devs wonderful things like joins, aggregates, window functions, views, foreign data wrappers, user defined functions and all of this other stuff the have to build themselves on top of their "easy" NoSQL DB. There are many, many good use cases for document DBs, where they win on latency and scale. I've just seen a lot of NoSQL by default designs, or NeverSQL attitude, and those end up being very expensive to actually develop and own.
SELECT 2+2 as num FROM <table> HAVING num > 3;
this will return as many 4s as there are rows in the table SELECT 2+2 as num FROM <table> HAVING num > 4;
this will return 0 rows. How is this not using something from SELECT in a HAVING clause?In MySQL you can use HAVING, ORDER BY, and GROUP BY with column aliases but not WHERE: https://stackoverflow.com/questions/942571/using-column-alia...
In PostgreSQL you can not use the column alias within WHERE or HAVING but you can use it in ORDER BY and GROUP BY: https://dba.stackexchange.com/questions/225874/using-column-...
Similarly in Microsoft SQL server you cannot use it in WHERE, HAVING, or GROUP BY but you can use it in ORDER BY.
In SQLite you can use column aliases within WHERE, HAVING, ORDER BY and GROUP BY: https://stackoverflow.com/questions/10923107/using-a-column-...
In Google Bigquery you can use them in GROUP BY and ORDER BY but in Hive you can only use them in ORDER BY.
As far as I know the standard only defines that you cannot use column aliases in the WHERE clause. I'm sure someone else can chime in with what the standard says about column aliases in ORDER BY, GROUP BY and HAVING.
If a sql dialect doesn't require the GROUP BY then I think the entire select result is one group. So you'd only get either 1 or 0 rows ever.
Or so I think :-)
Great article, thanks.
The reason is for that is Intellisense, you cannot show columns for SELECT unless you already know the table(s) you're selecting the columns for.
with data as
(
select *
from
(
values(1), (2), (0), (4)
) as abc(d)
)
select 1 / d
from data
-- where d <> 0
gives divide by zero. If you uncomment the where it then seems to work correctly.Well it doesn't. You cannot rely on this for MSSQL2005 or later. When the expressin grows more complex it blows up - I am saying this from losing days of work by finding out the hard way.
I'll repeat myself, the expressions in the select and that in the where can be evaluated in parallel. Thank you Microsoft.
Microsoft's solution to this is to expect the programmer to deal with it by skipping the evaluation of the expression
with data as
(
select *
from
(
values(1), (2), (0), (4)
) as abc(d)
)
select case when d <> 0 then 1 / d else null end
from data
where d <> 0;
The case prevents any possible division by zero.I've had this behaviour confirmed by Microsoft, and the supposed fix was their solution.
Not coincidentally I'm looking at learning postgres. For this and other reasons I don't want to work with MSSQL any more.
I think the main difference is in the level of curiosity/caring the person exhibits when they get a result you don't expect. When they do something that they think should work and it doesn't, do they carefully figure out the system behind it and why?
The users that do will write better queries faster, and the others will will just keep getting frustrated and foist the work onto others.
That's why I say its fundamentally about a person's curiosity and ability to self teach.
I too think its great people are learning in a way that is easy to absorb. I had to figure that out from an oracle manual and it wasn't great.
It's getting on for 20 years since I did CS and I'm certain there's stuff I've forgotten about (I've definitely forgotten half the stuff about network topologies but most of that stuff was about coax networks rather than ethernet so it's not stuff I've much since)
I find criticising people for having blind spots that we might consider obvious isn't a great way to share knowledge. Not only does it make individuals less willing to come forward with questions but it also means they're less likely to correct your errors (we are all fallible) for fear of being chastised again.
How deep down should your bog standard web developer know?
Yes, at one point I did write x86 (and 65C02 assembly before that)
Then we get into the whole “Mo one person knows how to make a pencil.”
Many times you encounter such knowledge when you are not ready for it, so you forget having encountered it. A good way to convince yourself of this is to start opening up some of your old college text books and look at the chapter introductions and conclusions. You will find some insights that you simply missed regardless of how you performed in the classes.
During the first few years of exposure to computer science, I definitely lacked the necessary context to understand certain concepts when I encountered them for the first time. For example, I had a theory of computation course that was all about regular expressions, regular languages, context free grammars, etc. At the same time as I took this course, I was also building my first iPhone app, and I often thought "none of this seems applicable". And then two years later I wrote a compiler to implement a subset of Java and I massively reassessed my opinion on the value of understanding the theory of computation and programming languages.
edit:
CTEs can make it closer to the syntax in question though.
If from was first, autocomplete could help you with the names of columns, like RStudio does with dplyr:
data_frame_name %>% select(column_name)
Because you start by piping the data reference into your select function, RStudio can inspect the data and autocomplete the column names in the select statement, completely opposite of what is possible inSQLWith a free order, you'd be able to start SQL queries with `FROM table_name SELECT ...` and have columns autocompleted.
Maybe "Order of Operations"? Similar to order of operations in math where you do (), then * and /, then + and -, ...
Yes, you can by wrapping the entire SELECT in another SELECT (or use WITH)
select *
from (select ...) as x
where ...
This works because the result of a SELECT is a (virtual) table on its own. It was an eye opener when I saw it for the first time.We use for our apps Kendo grids at work which allows you to create any report by dynamically choosing the filters and columns you want. On the server side a query is composed from the parameters the user chooses, including only the columns that are necessary. The data source for the query is a inline table valued function (TVF).
At one point we had trouble with performance after going live, and it could not be explained by changes to the TVF. Even when we reverted the released TVF changes the issues kept happening.
A day or so later I was with a database expert one day long optimising the query (may I plug the excellent free Sentry One tool here?). Eventually we had optimised the query. However, when we put the query back into the TVF performance was down again. The slightest things that changed affected the performance.
The most common causes for this in MySQL (and I expect SQL Server too) is that often your query can be resolved on multiple indexes, and picking which one is a matter of a heuristic. On MySQL changing table statistics can change which index is used, which can drastically affect query performance.
So a common poweruser optimization strategy (which should be used very carefully) is FORCE INDEX, also known as "I know better which index to use". More often than not I've found uses of FORCE INDEX that were more harmful than not.
But an ever more insidious version of this is queries that retrieve cached data being faster. This is something you can generally only observe in production because what's in cache is a matter of other traffic on the server.
So you can easily get cases where something looks much worse under EXPLAIN, but is faster. E.g. it can be faster to execute a "worse" query with no indexes than one with, if the one with no indexes happens to need data that's already in memory v.s. sitting on disk.
When you type the fields you want to select, there is zero context as to what those fields apply to, meaning that there is zero contextual help that you could get from know the table and group by ahead of typing select.
It's absolutely useful as a debugging tool to build up some semantic understanding of what the query means, and I encourage every database user to learn to use EXPLAIN, but relying on a mental execution model is borderline dangerous.
Database engines don’t actually literally run queries in this order because they implement a bunch of optimizations to make queries run faster – we’ll get to that a little later in the post.
So:
- you can use this diagram when you just want to understand which queries are valid and how to reason about what results of a given query will be
- you shouldn’t use this diagram to reason about query performance or anything involving indexes, that’s a much more complicated thing with a lot more variables
She claims this is a useful tool to understand the denotational semantics of the query and which kinds of queries are allowed vs. not allowed—and she's absolutely right.
As a quick demo, pick your favorite table and run:
SELECT 2+2 as num FROM <table> HAVING num > 3;
this will return as many 4s as there are rows in the table SELECT 2+2 as num FROM <table> HAVING num > 4;
this will return 0 rows.The diagram in the OP is a pretty good general/semantic understanding of SQL execution order for writing queries.
SELECT 2 + 2 FROM <table> HAVING 2 + 2 > 3;
SELECT 2 + 2 FROM <table> HAVING 2 + 2 > 4;
from u in User, where: u.id = 1, select: u.nameThis acronym has "ORDER BY" happen before "SELECT", but this article have them the other way around. Does this difference ever matter?
with INSERT and UPDATE the user provides a table as context for autocompletion of the column list.
There's been a billion revisions of ANSI SQL and they've never provided alternate ordering options. Who cares about users?
The title is missing a word. Interestingly, part of the industry is moving towards databases that optimize themselves (ex Snowflake & Oracle Autonomous Database), plus projects that do it for you (Apache Calcite).
Being able to break down and compose a query in your own defined order while leveraging a library to transform it into a valid query is quite nice for that way of thinking.
To dig deeper: https://en.wikipedia.org/wiki/Query_plan https://www.khanacademy.org/computing/computer-programming/s...
so, SQL is not about how you're going to get the results it's about what results you want.
...even Mongo's JSON-based query language has more elegance to it. SQL seems designed on purpose to be annoying to write, hard to read, and never look like anything, not the math logic, not any query operations that could ever be executed. I may be superficially similar to English, but English is horrible, why would you model a notation after that?!
Everytime I see someone yelling that Haskell or Lisp syntax is weird and unintuitive I think they need to have their face bashed into a printout of a complex SQL query!
From simple selects using some common functions and basic types, to filtering, to aggregations and grouping and finally joins. That gets them about 80% there for the kind of information they want ad-hoc.
Only with joins did the people really struggle for some time.
In my opinion the distinguishing factor wasn't the language, but the peoples willingness to learn and ability to apply it in practice on a somewhat regular basis.
I've always done data heavy work, but as a recently hired data engineer, I'm really diving deeply into this layer that previously I had taken for granted. In doing so, I find SQL highly expressive and beautiful for data work; much moreso than Python, my other primary language.
Which languages do you feel manipulate data beautifully?
[0] - https://people.cs.umass.edu/~yanlei/courses/CS691LL-f06/pape...
But I'd just want to write code like:
(employees * addresses)[employee.name, address.city.name]
| filter{address.city.name = "London"}
...that would match rel algebra notation nicely, and extend to complex cases more like: ( employees e *{e.id = a.employee_id} addresses a )
| filter{a.city.name = "London"}
| [e.name, a.city.name]
...you can replace operators with words of course. Or you could represent this as a JSON structure, even better imo. But the point would be to have a notation highlighting the idea of a "product", of restricting and specializing that product or specifying how it's made, and of indexing into its fields. The way I see it in my mind is like taking "a (maybe generalized) cartesian product" of different things, then filtering this, then zooming on each dot/point/object and filtering its fields etc. And obviously focusing on piping/chaining stuff.Maybe the notation I suggest is not a good one, but I'd care more about making the concepts obvious and readable and composable.
select name, count(*) from table group by 1
as select name, count(*) from table group by name
before executing the query ORDER BY NULL
would defeat something MySQL was getting wrong in a query that already wasn't ordered by anything. A coworker put it as "waving a dead chicken that doesn't even exist".Or they did when I was using Oracle.
I generally dislike having multiple ways to achieve the same end result.
Has that ever not been true?
It is a bit of a "well, duh" thing, but I've had my fair share of those with SQL through the years so I won't hold it against the author.
One of my more recent random discoveries was that I could join a subquery, ie
select x.a, y.b, y.c
from tableX x
join (
select id, sum(b) as b, count(b) as c
from tableY group by id
) as y on y.id = x.id
Again nothing special, just hadn't had the need for it before so didn't even think about the possibility. select * from (select 1) as temp; SELECT * FROM table
…well, modulo the fact that in a concrete sense that substitution would lead to infinite recursion!What the author was not aware of was what the actual execution order is. This article is about them actually digging in to find out what that order was.
You start explaining what you need and work backwards, on the other end, they work in reverse, because that's how it's stored.
Conceptually, it's like piping in bash, "find | grep | cut". Start with finding the files(tables), grep for the row data (where), cut the fields you want returned (select).
drop schema if exists *
Doesn't start with select; you never have to write sql again;
you're probably firedI was very fortunate, we had backups and restoring went smoothly, and my boss very calmly asked me one thing: "did you learn from this?" I assured him that I had.
JOINs are imperative.
I'm just saying they are not declarative since you have to explicitly describe how each table is joined to the next, the operation goes beyond the query itself.