Skip to content

F# Guide — Query Expressions: LINQ-Style Data Queries

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about F# Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

F# query expressions provide a LINQ-like syntax for querying data sources using comprehension syntax with select, where, groupBy, join, and sorting operations.

What You'll Learn

  • Query expression syntax
  • Select, where, and sorting
  • Grouping and aggregation
  • Joining data sources
  • Database queries with query expressions

Why It Matters

Query expressions bridge functional operations with database queries, enabling type-safe queries against SQL, entity frameworks, and other queryable sources. Durga Antivirus Pro uses queries for log analysis.

Real-World Use

Querying SQL databases, filtering large in-memory collections, generating reports, and data analysis.

flowchart LR
    A["Query Expressions"] --> B["Syntax"]
    B --> C["Operations"]
    C --> D["Grouping"]
    D --> E["Joins"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Basic Query

open System.Linq

let data = [1; 2; 3; 4; 5; 6]

let query = query {
    for x in data do
    where (x > 3)
    sortBy x
    select x
}

let results = query |> Seq.toList
// [4; 5; 6]

Select

let numbers = [1..10]

let doubled = query {
    for n in numbers do
    select (n * 2)
}
// [2; 4; 6; 8; 10; 12; 14; 16; 18; 20]

let namedResults = query {
    for n in numbers do
    select {| Original = n; Squared = n * n |}
}

Filtering

let data = [1..20]

let filtered = query {
    for x in data do
    where (x % 2 = 0)
    where (x > 10)
    sortByDescending x
    select x
}
// [20; 18; 16; 14; 12]

// Or combine conditions
let combined = query {
    for x in data do
    where (x > 5 && x < 15)
    select x
}

Grouping

type Person = { Name: string; Age: int; Dept: string }

let people = [
    { Name = "Alice"; Age = 30; Dept = "Eng" }
    { Name = "Bob"; Age = 25; Dept = "Eng" }
    { Name = "Carol"; Age = 35; Dept = "Sales" }
]

let grouped = query {
    for p in people do
    groupBy p.Dept into g
    select (g.Key, g |> Seq.length)
}
// [("Eng", 2); ("Sales", 1)]

let withAverage = query {
    for p in people do
    groupBy p.Dept into g
    select (g.Key, g |> Seq.averageBy (fun p -> float p.Age))
}

Joins

type Dept = { Id: int; Name: string }
type Employee = { Id: int; Name: string; DeptId: int }

let depts = [{Id=1; Name="Eng"}; {Id=2; Name="Sales"}]
let emps = [{Id=1; Name="Alice"; DeptId=1}; {Id=2; Name="Bob"; DeptId=2}]

let joined = query {
    for e in emps do
    join d in depts on (e.DeptId = d.Id)
    select (e.Name, d.Name)
}
// [("Alice", "Eng"); ("Bob", "Sales")]

// Left join
let leftJoin = query {
    for e in emps do
    leftOuterJoin d in depts on (e.DeptId = d.Id) into grouping
    for d in grouping.DefaultIfEmpty() do
    select (e.Name, d |> Option.map (fun d -> d.Name))
}

Aggregation

let sales = [100.0; 200.0; 150.0; 300.0; 50.0]

let aggregates = query {
    for s in sales do
    select (s)
    count
}
// 5

let sum = query { for s in sales do sumBy s }
// 800.0

let avg = query { for s in sales do averageBy (float s) }
// 160.0

Distinct and Take

let items = [1; 1; 2; 3; 2; 4; 5]

let distinct = query {
    for i in items do
    distinct
    select i
}
// [1; 2; 3; 4; 5]

let firstThree = query {
    for i in items do
    take 3
    select i
}
// [1; 1; 2]

Common Mistakes

1. Forgetting to evaluate

Query expressions are lazy. Use Seq.toList or Seq.toArray to force evaluation.

2. Multiple from vs join

Use from for cross product, join for inner join with condition.

3. Using query on non-queryable data

Query expressions work on IEnumerable. Use query { } for in-memory queries, LINQ to SQL for databases.

4. Complex grouping

Use groupBy with into to continue the query after grouping.

5. Mixing query and collection functions

Query expressions are separate from List/Seq functions. Choose one approach consistently.

Practice Questions

1. What is a query expression? A syntax for querying data sources using comprehension syntax with operations like select, where, groupBy, and join.

2. How does groupBy work in queries? Group elements by a key, then continue the query with into groupName. Access .Key for group key.

3. When should you use queries vs List functions? Use queries for SQL-like operations and databases. Use List/Seq functions for in-memory transformations.

Challenge: Write a query that joins orders with customers and groups by customer.

FAQ

{{< faq question="Are F# queries efficient?" >}} Yes. Queries are compiled to LINQ expressions that can be translated to SQL or executed efficiently on in-memory data. {{< /faq >}}

{{< faq question="Can I use queries with databases?" >}} Yes, using query expressions with Entity Framework or other LINQ providers. The query compiles to SQL. {{< /faq >}}

{{< faq question="What is the difference between query and Seq?" >}} Query expressions have SQL-like syntax and support operations like groupBy and join more naturally than Seq functions. {{< /faq >}}

{{< faq question="Can I page results?" >} Yes, use skip n and take n for paging: query { for x in data do skip 10; take 10; select x }. {{< /faq >}}

{{< faq question="Do queries support nested queries?" >} Yes. You can nest query expressions inside other queries for subquery functionality. {{< /faq >}}

Mini Project

Build a reporting query for employee data:

type Employee = { Name: string; Salary: decimal; Dept: string; Years: int }

let employees = [
    { Name = "Alice"; Salary = 80000m; Dept = "Eng"; Years = 5 }
    { Name = "Bob"; Salary = 60000m; Dept = "Eng"; Years = 2 }
    { Name = "Carol"; Salary = 90000m; Dept = "Sales"; Years = 8 }
]

let deptReport = query {
    for e in employees do
    groupBy e.Dept into g
    select {| Dept = g.Key
               Count = g |> Seq.length
               AvgSalary = g |> Seq.averageBy (fun e -> e.Salary)
               MaxYears = g |> Seq.maxBy (fun e -> e.Years) |}
}

What's Next

Now that you understand query expressions, explore type providers for data integration.

Topic Description Link
F# Type Providers Data type providers {{< ref "22-type-providers" >}}
F# File I/O File operations {{< ref "23-file-io" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro