查询操作 #

一、查询基础 #

1.1 基本查询 #

elixir
import Ecto.Query

def list_users do
  Repo.all(User)
end

def list_users_query do
  query = from(u in User, select: u)
  Repo.all(query)
end

1.2 选择字段 #

elixir
def list_user_emails do
  from(u in User, select: u.email)
  |> Repo.all()
end

def list_user_summaries do
  from(u in User, select: %{id: u.id, name: u.name, email: u.email})
  |> Repo.all()
end

1.3 选择所有字段 #

elixir
def list_users do
  from(u in User)
  |> Repo.all()
end

二、条件查询 #

2.1 where子句 #

elixir
def get_active_users do
  from(u in User, where: u.active == true)
  |> Repo.all()
end

def get_users_by_age(min_age, max_age) do
  from(u in User, where: u.age >= ^min_age and u.age <= ^max_age)
  |> Repo.all()
end

2.2 多条件 #

elixir
def search_users(opts) do
  query = from(u in User)

  query = if opts[:name] do
    from(u in query, where: ilike(u.name, ^"%#{opts[:name]}%"))
  else
    query
  end

  query = if opts[:active] do
    from(u in query, where: u.active == ^opts[:active])
  else
    query
  end

  Repo.all(query)
end

2.3 in操作 #

elixir
def get_users_by_ids(ids) do
  from(u in User, where: u.id in ^ids)
  |> Repo.all()
end

2.4 is_nil操作 #

elixir
def get_users_without_email do
  from(u in User, where: is_nil(u.email))
  |> Repo.all()
end

def get_users_with_email do
  from(u in User, where: not is_nil(u.email))
  |> Repo.all()
end

2.5 like和ilike #

elixir
def search_by_name(name) do
  from(u in User, where: ilike(u.name, ^"%#{name}%"))
  |> Repo.all()
end

三、排序 #

3.1 order_by #

elixir
def list_users_ordered do
  from(u in User, order_by: [desc: u.inserted_at])
  |> Repo.all()
end

def list_users_multi_order do
  from(u in User, order_by: [asc: u.name, desc: u.inserted_at])
  |> Repo.all()
end

3.2 动态排序 #

elixir
def list_users(opts) do
  order = opts[:order] || :inserted_at
  direction = opts[:direction] || :desc

  from(u in User, order_by: [{^direction, ^order}])
  |> Repo.all()
end

四、分页 #

4.1 limit和offset #

elixir
def list_users_paginated(page, per_page) do
  offset = (page - 1) * per_page

  from(u in User,
    limit: ^per_page,
    offset: ^offset,
    order_by: [desc: u.inserted_at]
  )
  |> Repo.all()
end

4.2 分页辅助函数 #

elixir
def paginate(query, page, per_page) do
  offset = (page - 1) * per_page

  from(q in query,
    limit: ^per_page,
    offset: ^offset
  )
end

def list_users(page \\ 1, per_page \\ 10) do
  from(u in User, order_by: [desc: u.inserted_at])
  |> paginate(page, per_page)
  |> Repo.all()
end

4.3 总数计算 #

elixir
def list_users_with_count(page \\ 1, per_page \\ 10) do
  query = from(u in User)

  total = from(q in query, select: count(q.id)) |> Repo.one()
  entries = query |> paginate(page, per_page) |> Repo.all()
  total_pages = ceil(total / per_page)

  %{
    entries: entries,
    total: total,
    page: page,
    per_page: per_page,
    total_pages: total_pages
  }
end

五、聚合函数 #

5.1 count #

elixir
def count_users do
  from(u in User, select: count(u.id))
  |> Repo.one()
end

def count_active_users do
  from(u in User, where: u.active == true, select: count(u.id))
  |> Repo.one()
end

5.2 sum, avg, min, max #

elixir
def user_stats do
  from(u in User,
    select: %{
      total: count(u.id),
      avg_age: avg(u.age),
      min_age: min(u.age),
      max_age: max(u.age)
    }
  )
  |> Repo.one()
end

5.3 group_by #

elixir
def users_count_by_status do
  from(u in User,
    group_by: u.active,
    select: {u.active, count(u.id)}
  )
  |> Repo.all()
end

def posts_count_by_author do
  from(p in Post,
    group_by: p.author_id,
    select: {p.author_id, count(p.id)},
    order_by: [desc: count(p.id)]
  )
  |> Repo.all()
end

5.4 having #

elixir
def authors_with_many_posts(min_posts) do
  from(p in Post,
    group_by: p.author_id,
    having: count(p.id) > ^min_posts,
    select: {p.author_id, count(p.id)}
  )
  |> Repo.all()
end

六、关联查询 #

6.1 join #

elixir
def posts_with_authors do
  from(p in Post,
    join: u in User,
    on: p.author_id == u.id,
    select: %{post: p, author: u}
  )
  |> Repo.all()
end

6.2 多表关联 #

elixir
def comments_with_post_and_author do
  from(c in Comment,
    join: p in Post, on: c.post_id == p.id,
    join: u in User, on: p.author_id == u.id,
    select: %{comment: c, post: p, author: u}
  )
  |> Repo.all()
end

6.3 left_join #

elixir
def posts_with_comments_count do
  from(p in Post,
    left_join: c in Comment,
    on: c.post_id == p.id,
    group_by: p.id,
    select: {p, count(c.id)}
  )
  |> Repo.all()
end

七、预加载 #

7.1 基本预加载 #

elixir
def list_posts_with_comments do
  from(p in Post, preload: [:comments])
  |> Repo.all()
end

def list_posts_with_author do
  from(p in Post, preload: [:author])
  |> Repo.all()
end

7.2 多重预加载 #

elixir
def list_posts_full do
  from(p in Post, preload: [:author, :comments, :tags])
  |> Repo.all()
end

7.3 嵌套预加载 #

elixir
def list_posts_with_comments_and_authors do
  from(p in Post, preload: [comments: :author])
  |> Repo.all()
end

7.4 条件预加载 #

elixir
def list_posts_with_recent_comments do
  from(p in Post,
    preload: [
      comments: from(c in Comment, where: c.inserted_at > ago(7, "day"))
    ]
  )
  |> Repo.all()
end

八、子查询 #

8.1 基本子查询 #

elixir
def users_with_posts do
  subquery = from(p in Post, select: p.author_id)

  from(u in User, where: u.id in subquery(subquery))
  |> Repo.all()
end

8.2 exists子查询 #

elixir
def users_with_published_posts do
  from(u in User,
    where: exists(from(p in Post, where: p.author_id == u.id and p.published == true))
  )
  |> Repo.all()
end

九、片段查询 #

9.1 使用fragment #

elixir
def search_by_full_name(name) do
  from(u in User,
    where: fragment("concat(?, ' ', ?) ilike ?", u.first_name, u.last_name, ^"%#{name}%")
  )
  |> Repo.all()
end

9.2 原始SQL #

elixir
def execute_raw_sql do
  Ecto.Adapters.SQL.query!(Repo, "SELECT * FROM users WHERE age > $1", [18])
end

十、动态查询 #

10.1 动态条件 #

elixir
def search_users(filters) do
  query = from(u in User)

  query = Enum.reduce(filters, query, fn
    {:name, name}, q ->
      from(u in q, where: ilike(u.name, ^"%#{name}%"))

    {:email, email}, q ->
      from(u in q, where: ilike(u.email, ^"%#{email}%"))

    {:active, active}, q ->
      from(u in q, where: u.active == ^active)

    _, q -> q
  end)

  Repo.all(query)
end

10.2 动态排序 #

elixir
def sort_by(query, field, direction) do
  from(q in query, order_by: [{^direction, ^field}])
end

十一、查询组合 #

11.1 union #

elixir
def all_content do
  posts = from(p in Post, select: %{id: p.id, title: p.title, type: "post"})
  pages = from(p in Page, select: %{id: p.id, title: p.title, type: "page"})

  from(q in union(posts, ^pages))
  |> Repo.all()
end

11.2 except #

elixir
def users_without_admins do
  all_users = from(u in User, select: u.id)
  admins = from(u in User, where: u.role == "admin", select: u.id)

  from(u in User, where: u.id in ^all_users and u.id not in ^admins)
  |> Repo.all()
end

十二、总结 #

12.1 核心操作 #

操作 说明
from 创建查询
where 条件过滤
select 选择字段
order_by 排序
limit/offset 分页
join 关联查询
preload 预加载
group_by 分组

12.2 查询模式 #

elixir
from(u in User,
  where: u.active == true,
  select: u,
  order_by: [desc: u.inserted_at],
  limit: 10
)
|> Repo.all()

12.3 下一步 #

现在你已经了解了查询操作,接下来让我们学习 关联关系,深入了解数据关联!

最后更新:2026-03-28