从SQL查询中填充虚拟字段

问题描述 投票:1回答:1

我必须处理无法更改的数据库设置,并且必须使用特定的SQL查询来计算不是表中字段的值(例如SELECT *, 1 AS x FROM products; x是我需要的字段)。如何在Ecto中进行这项工作?这是我的方法以及遇到的问题:

设置

$ mix phx.new testapp
$ cd testapp
$ mix ecto.create
$ mix phx.gen.html Shops Product products name price:float
$ mix ecto.migrate

之后,我创建了两个产品。

x

我将虚拟x字段添加到product

lib / testapp / shops / product.ex

defmodule Testapp.Shops.Product do
  use Ecto.Schema
  import Ecto.Changeset

  schema "products" do
    field :name, :string
    field :price, :float
    field :x, :integer, virtual: true  # <-----

    timestamps()
  end

  @doc false
  def changeset(product, attrs) do
    product
    |> cast(attrs, [:name, :price])
    |> validate_required([:name, :price])
  end
end

并且我将以下功能添加到Testapp.Shops

def execute_and_load(sql, params, model) do
  result = Ecto.Adapters.SQL.query!(Repo, sql, params)
  Enum.map(result.rows, &Repo.load(model, {result.columns, &1}))
end

def list_products_with_x do
  sql = "SELECT *, 1 AS x FROM products;" # <- simplified
  execute_and_load(sql, [], Testapp.Shops.Product)
end

1 AS x,整个SQL查询只是一个简化示例!在实际的应用程序中,我必须使用SQL查询来调用存储过程进行计算,该计算会将值存储在x中。因此,会有一些我无法使用Ecto本身创建的SQL。我必须使用SQL。

问题

SQL查询为每个条目传递x的值,但productx列出为nil。我怎么解决这个问题?如何在virtual中填写execute_and_load/3字段?

iex(1)> Testapp.Shops.list_products_with_x
[debug] QUERY OK db=1.3ms queue=2.2ms idle=8177.7ms
SELECT *, 1 AS x FROM products; []
[
  %Testapp.Shops.Product{
    __meta__: #Ecto.Schema.Metadata<:loaded, "products">,
    id: 1,
    inserted_at: ~N[2020-02-12 07:29:36],
    name: "Apple",
    price: 0.5,
    updated_at: ~N[2020-02-12 07:29:36],
    x: nil
  },
  %Testapp.Shops.Product{
    __meta__: #Ecto.Schema.Metadata<:loaded, "products">,
    id: 2,
    inserted_at: ~N[2020-02-12 07:29:47],
    name: "Orange",
    price: 0.75,
    updated_at: ~N[2020-02-12 07:29:47],
    x: nil
  }
]

我愿意为给定的问题寻求替代解决方案。我无法在我的Elixir程序中计算x的值。我必须使用SQL来计算它,我想使用Ecto。

elixir phoenix-framework ecto
1个回答
0
投票

一种更好的方法是选择结构所需的所有内容,然后将其移至Ecto.Struct。我的方法如下:

def get_products() do
    query = from p in Products,
            select: %{name: p.name, price: p.price, x: fragment("1")}
    query
    |> Repo.all()
    |> Enum.map(fn el -> struct(Products, el) end)
  end

这种方法的优点是我不使用原始字符串查询。您的计算应放在片段部分的内部。

© www.soinside.com 2019 - 2024. All rights reserved.