Linq does this [0], maybe partially because it's SQL-like but built for working with enumerables instead so they could do what they wanted. One other quirk of SQL ordering that always gets me is that SET comes before WHERE in UPDATE. I always get terrified that I'll run without the WHERE or without selecting the WHERE. Thankfully a good database tool like DataGrip will yell at you if you try to do a modifying operation without a WHERE.
SQL could also have done what they wanted, for example allowing both
FROM foo
WHERE baz = quux
SELECT bar
and even (the equivalent linq wont compile because ‘baz’ isn’t there anymore when the ‘where’ runs)
FROM foo
SELECT bar
WHERE baz = quux
However, SQL predates smart code completion being an expected language feature, so they went for the feature “looks like normal American English” (“from the kitchen, can you get me the scales?” is less common than “can you get me the scales from the kitchen?” or “can you get me the scales? They’re in the kitchen”) instead of “make a grammar where smart code completion works well”.
I can't remember where I read it, but I believe one of the main reasons why Linq moved the FROM clause to the front was for better intellisense. If you start writing SELECT x in an IDE, there really is no way of providing intellisense for x.
However if you write FROM table SELECT x, the IDE can first provide intellisense for table names while you write your FROM clause. Then it can provide intellisense for the SELECT clause based on columns in the tables/views/etc you listed in your FROM. So it was basically done for better UX.
I believe LINQ maps to a chains of function calls on an enumerable, with the functions usually taking anonymous functions as input (and then the series of functions is rewritten into a SQL statement by EF to hit the DB). I don’t think you’d be able to support type analysis if you didn’t specify the enumerable upfront
Eg
Mylist.select(x => …) can determine the type of x, because it has the type of mylist —> List<T>
Select(x => …).from(mylist) and you simply can’t determine x, because C# type analysis can’t go backwards.
So I think it’s less a UX question and more of an absolute requirement for the API to work at all. The alternative would be a SelectFrom(x => …, mylist) function so it can be submitted in one shot, essentially what SQL is doing, but that’s disgusting — who doesn’t love function chaining?
No, you would be able to support type analysis just as easily because desugaring
select x => ... from mylist;
into
mylist.SelectMany(x => ...);
is straightforward: C# compiler is not expected to be single-pass, it has an AST to operate upon. You'll have a LinqSelectNode with Projection (a lambda expression), Filter, and Source fields which you replace with a new ExtensionMethodCall{ Lhs = origNode.Source, MethodName = "SelectMany", Args = new []Node{ origNode.Projection }), easy.
In terms of actually writing queries live, like if you're in the CLI client, the order of UPDATE is definitely terrifying.
The workarounds of writing it out of order or as a SELECT first are fine... I'd almost like to see a mode the interactive client sets that just rejects any UPDATE without a WHERE, and you'd have to do WHERE 1 or similar to get an "UPDATE everything."
If you are doing an update the first word is actually BEGIN.
/* use your PPE */
BEGIN;
/* make the change */
UPDATE foo SET bar = ‘baz’;
/* sanity check */
SELECT * FROM foo WHERE bar != ‘baz’;
/* oops, let’s pretend this never happened */
ROLLBACK;
This terrifies me as well. The workaround is to write the WHERE clause before the SET clause. If you inadvertently submit the query partway, it will be invalid and it's not a big deal.
If you have to use TSQL like me, you can use BEGIN/ROLLBACK TRAN with an OUTPUT clause to easily confirm, then just change it to COMMIT. I actually like this workflow quite a lot
[0] https://learn.microsoft.com/en-us/dotnet/csharp/programming-...