WHERE Clause
You can filter your SELECT
queries with a WHERE
clause. In Fluent SQL, the Where()
method can be chained from a select statement.
var selectStatement =
Select.All("ID", "Name")
.From("Customer")
.Where("Country".IsEqualTo("NZ".Literal()));
SELECT [ID], [Name]
FROM [Customer]
WHERE [Country] = 'NZ';
There are a couple of things to note here. We can’t always write exactly what we want to write. The Fluent SQL interface is as close to spoken English as we can make it but we can’t make it the same as written SQL. We can’t write .Where("Country" = "NZ")
for two reasons.
First, it’s not legal C#. To the C# compiler, that looks like an assignment and the left-hand side would need to be a variable. If we used ==
, that would be a legal C# predicate, but would always evaluate to false
at compile-time, which is not what we want. So we have the IsEqualTo()
extension method.
Second, we need to differentiate between strings that represent column names and strings that represent string literals. For a reminder, see the String Literals section on the SELECT Statement page.
Comparison Operators
There are extensions methods for all the other comparison operators: IsNotEqualTo()
, IsGreaterThan()
, IsGreaterThanOrEqualTo()
, IsLessThan()
, IsLessThanOrEqualTo()
, and Like()
.
Optional Filters
Perhaps filtering is optional. Sometimes the query should be filtered; sometimes not.
Fluent SQL uses the C# Functional Extensions library, which includes the Maybe
type. Normally, the Where()
method takes a Predicate
as its argument. (You don’t often need to know this - type inference handles the details.) There’s an overload of Where()
that takes a Maybe<Predicate>
, an optional predicate. The argument may or may not exist. It allows you to optionally add a WHERE
clause without breaking the fluency of the code.
In the C# code below, filter
ia a Maybe<Predicate>
that has a value if useFilter
is true and has no value if it’s false.
var filter = useFilter ?
"Country".IsEqualTo("NZ".Literal()) :
Maybe<Predicate>.None;
var selectStatement =
Select.All("ID", "Name")
.From("Customer")
.Where(filter);
When useFilter
is true, we get a query with a WHERE
clause.
SELECT [ID], [Name]
FROM [Customer]
WHERE [Country] = 'NZ';
When useFilter
is false, there is no WHERE
clause.
SELECT [ID], [Name]
FROM [Customer];
There’s no branching in your Fluent SQL code and you can chain other methods, like GroupBy()
and OrderBy()
as normal.
Logical Operators
Predicates can be combined with logical operators. These can be arbitrarily complex. If the predicates are nested, Fluent SQL will respect the nesting and add parentheses where they’re needed.
For example, to filter a query to customers in either New Zealand or Australia whose names begin with ‘B’:
var selectStatement =
Select.All("ID", "Name")
.From("Customer")
.Where(
("Country".IsEqualTo("NZ".Literal())
.Or("Country".IsEqualTo("AU".Literal())))
.And("Name".Like("B%"))
);
SELECT [ID], [Name]
FROM [Customer]
WHERE ([Country] = 'NZ' OR [Country] = 'AU') AND [Name] LIKE 'B%';
Other Predicates
As well as comparison and logical expressions, other predicates are available. Fluent SQL has methods IsNull()
, IsNotNull()
, Between()
, NotBetween()
, In()
, and NotIn()
.
IS NULL
IsNull()
and IsNotNull()
take no arguments.
var selectStatement =
Select.All("ID", "Name")
.From("Customer")
.Where("Name".IsNull());
SELECT [ID], [Name]
FROM [Customer]
WHERE [Name] IS NULL;
BETWEEN
Between()
and NotBetween()
take two arguments, a begin expresison and an end expresison.
var selectStatement =
Select.All("ID", "Name")
.From("Customer")
.Where("CreditLimit".Between(1000, 10_000));
SELECT [ID], [Name]
FROM [Customer]
WHERE [CreditLimit] BETWEEN 1000 AND 10000;
IN
In()
and NotIn()
can take a sequence of expressions, either as a comma-separated list or an array.
var selectStatement =
Select.All("ID", "Name")
.From("Customer")
.Where("Country".In("NZ".Literal(), "AU".Literal(), "FJ".Literal()));
var countries = new[] {"NZ", "AU", "FJ"}.Select(c => c.Literal()).ToArray();
var selectStatement =
Select.All("ID", "Name")
.From("Customer")
.Where("Country".In(countries));
SELECT [ID], [Name]
FROM [Customer]
WHERE [Country] IN ('NZ', 'AU', 'FJ');
In()
and NotIn()
can also take a subquery, i.e. another select statement.
var selectStatement =
Select.All("ID", "Name")
.From("Customer")
.Where("Country".In(
Select.All("Alpha2Code")
.From("Country")
.Where("Region".IsEqualTo("Oceania".Literal()))
));
SELECT [ID], [Name]
FROM [Customer]
WHERE [Country] IN (
SELECT [Alpha2Code]
FROM [Country]
WHERE [Region] = 'Oceania'
);
You’d be better this subquery as a table join, but we’ll get to that.