postgresql - 在带有 Postgres 的 Elixir 中,我怎样才能让数据库返回未使用的枚举值?

标签 postgresql enums elixir ecto processing-efficiency

我有一个 EctoEnum.Postgres:

# @see: https://en.wikipedia.org/wiki/ISO_4217
defmodule PricingEngine.Pricing.CurrencyEnum do
  @options [
    :AED,
    :AFN,
    # snip...
    :ZWL
  ]
  use EctoEnum.Postgres,
    type: :currency,
    enums: @options

  def values, do: @options
end

此枚举已包含在我们的 Postgres 数据库中

我们还有一个结构:

defmodule PricingEngine.Pricing.Currency do
  use Ecto.Schema
  import Ecto.Changeset

  schema "currencies" do
    field(:currency, PricingEngine.Pricing.CurrencyEnum)

    timestamps()
  end

  @doc false
  def changeset(currency, attrs) do
    currency
    |> cast(attrs, [:currency])
    |> validate_required([:currency])
    |> unique_constraint(:currency)
  end
end

我们目前可以成功地使用以下函数来确定哪些货币是活跃/使用的:

  def active_currency_isos do
    Repo.all(select(Currency, [record], record.currency))
  end

  defdelegate all_currency_isos,
    to: CurrencyEnum,
    as: :values

  def inactive_currency_iso do
    Pricing.all_currency_isos() -- Pricing.active_currency_isos()
  end

这行得通,但我相信如果我们只向数据库询问此信息,效率会更高。

知道怎么做吗?

最佳答案

如果你想获得所有使用过的枚举的列表,你应该在货币字段上做一个distinct。这使用了 Postgres DISTINCT ON 运算符:

from(c in Currency,
 distinct: c.currency,
 select: c.currency
)

这将查询货币列唯一的表,并仅返回货币列值。您应该获得表中存在的所有枚举的数组。

以这种方式进行时存在一些效率问题,这可以通过物化 View 、查找表、内存缓存等来缓解。但是,如果您的数据集不是非常大,您应该能够将其用于一会儿。

编辑:

根据响应,我将展示如何获取未使用的枚举。

有两种方法可以做到这一点。

纯SQL

此查询将获取所有使用过的枚举,并与整个可用枚举集有所不同。我们用来执行此操作的运算符是 EXCEPT,您可以使用 enum_range 获取所有可用枚举的列表。我将使用 unnest 将枚举类型的数组转换为单独的行:

SELECT unnest(enum_range(NULL::currency)) AS unused_enums
EXCEPT (
    SELECT DISTINCT ON (c.name) c.name
    FROM currencies c
)

您可以通过执行以下操作在 Ecto 中执行此原始 SQL:

Ecto.Adapters.SQL.query!(MyApp.Repo, "SELECT unnest(...", [])

由此您将获得一个 Postgresx.Result,您必须从中获取值:

result
|> Map.get(:rows, [])
|> List.flatten()
|> Enum.map(&String.to_existing_atom/1)

我不太确定用纯 Ecto 编写这个查询的方法,但如果你想出来了请告诉我。

在代码中

您可以使用 distinct 执行我之前发布的第一个查询,然后在代码中执行不同的操作。

query = from(c in Currency,
  distinct: c.currency,
  select: c.currency
)

CurrencyEnum.__enums__() -- Repo.all(query)

任何一种方式在性能方面都可能微不足道,所以这取决于您。

关于postgresql - 在带有 Postgres 的 Elixir 中,我怎样才能让数据库返回未使用的枚举值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57587518/

相关文章:

django - 升级到 django 1.11 未找到多对多列

sql - PostgreSQL 组按 : SELECT column on MAX of another WHERE a third column = x

SQL - 从表中获取并与同一个表连接

enums - 为什么我不能从函数返回显式类型的结果枚举?

javascript - 将样式或js脚本附加到phoenix模板中的布局

arrays - 在具有大量输入值的数组中查找

c++ - 对大枚举值的表示是否有任何保证?

ios - 为什么我在 Swift 中设置时 Enum 的值没有改变?

elixir - 如何在插入时为 Ecto 模型中的某些字段设置一些值

elixir - 如何使用 `mix test` 列出所有测试