【问题标题】:Ecto fragment subquery with conditional where带有条件 where 的 Ecto 片段子查询
【发布时间】:2020-05-19 16:20:44
【问题描述】:

我正在尝试使用 ecto 执行带有子查询的查询作为SELECTs 之一。在 SQL 中它看起来像这样 (player has_many votes):

SELECT
    players.id AS player_id,
    (SELECT count(*) FROM votes WHERE votes.player_id = players.id) AS vote_count
FROM
    players

但是,根据参数的存在,我希望SELECT 子查询有一个额外的WHERE 子句。例如。

SELECT
    players.id AS player_id,
    (SELECT count(*) FROM votes WHERE votes.player_id = players.id AND votes.type = 'motm') AS vote_count
FROM
    players

在ecto中,我想出了这个:

vote_count_query =
  from(p in Player,
    select: %{
      player_id: p.id,
      vote_count:
        fragment(
          "SELECT count(*) FROM votes WHERE votes.player_id = ?",
          p.id
        )
    }
  )

假设有一个变量vote_type 可能是也可能不是nil,我如何有条件地将where 子句添加到内部选择子查询?例如

fragment(
  "SELECT count(*) FROM votes WHERE votes.player_id = ? AND votes.type = ?",
  p.id,
  ^vote_type
)

(如果有更好的方法来计算所有玩家的票数,那么我很高兴听到它。加入后,似乎没有票数的玩家不会被退回。 )

【问题讨论】:

  • fragment("SELECT count(*) FROM votes WHERE votes.player_id = ? AND (? IS NULL OR votes.type = ?)", p.id, ^vote_type, ^vote_type) 怎么样?

标签: sql elixir ecto


【解决方案1】:

虽然@dogbert 的the comment 看起来可能可行,但我设法通过将SELECT 子查询替换为左连接来实现这一点:

def vote_count(team_id, vote_type \\ nil) do
  vote_count_query =
    Player
    |> select([p, v], %{
      player_id: p.id,
      vote_count: count(v.id)
    })
    |> where([p, _], p.team_id == ^team_id)
    |> group_by([p, _], p.id)

  # depending on whether we are filtering by vote_type use the appropriate join
  # condition
  if(is_nil(vote_type),
    do: vote_count_query |> join(:left, [p], v in assoc(p, :votes)),
    else:
      vote_count_query
      |> join(:left, [p], v in Vote, on: p.id == v.player_id and v.type == ^vote_type)
  )
end

似乎关键是在连接条件中使用带有投票类型的 left 外连接,而不是默认的内连接。否则结果中不返回票数为 0 的玩家。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多