As Luke Hoban explains on his blog:

With let query clauses, you can introduce a variable into scope and use it in the subsequent query clauses. Similar to local variables in a method body, this gives you a way to avoid evaluating a common expression multiple times by storing it in a variable. This can be very useful even in much simpler queries. Of course, in the query above – let is absolutely critical.

The “query above” he refers to is a ray tracer coded as a big LINQ query full of let clauses!

Let’s see is an example from the official LINQ forum.
Someone asked how to query the following XML document:

<car name="Toyota Coupe">
<profile name="Vendor" value="Toyota"/>
<profile name="Model" value="Celica"/>
<profile name="Doors" value="2"/>
<support name="Racing" value="yes"/>
<support name="Towing" value="no"/>
<car name="Honda Accord Aerodec">
<profile name="Vendor" value="Honda"/>
<profile name="Model" value="Accord"/>
<profile name="Doors" value="4"/>
<support name="Racing" value="no"/>
<support name="Towing" value="yes"/>

Here is one way to do it:

from car in root.Elements("car")
let profiles =
from profile in car.Elements("profile")
select new {
Name = profile.Attribute("name").Value,
Value = profile.Attribute("value").Value
let supports =
from support in car.Elements("support")
select new {
Name = support.Attribute("name").Value,
Value = support.Attribute("value").Value
select new Car {
Name = car.Attribute("name").Value,
Vendor = profiles.Single(prof => prof.Name == "Vendor").Value,
Model = profiles.Single(prof => prof.Name == "Model").Value,
Doors = int.Parse(profiles.Single(prof => prof.Name == "Doors").Value),
RacingSupport = supports.Single(sup => sup.Name == "Racing").Value == "yes"

It’s easier to isolate the “profile” and “support” elements in separate sequences using let clauses, as above. Then the select clause becomes simple to write.