web-dev-qa-db-ja.com

Rectoでraw SQLを使用する方法

更新/挿入の要件があるため、postgresストアドプロシージャを呼び出すか、共通テーブル式を使用する必要があります。また、パスワードにpgcrypto拡張機能を使用し、postgres関数(パスワードをエンコード/デコードする「暗号化」など)を使用したいと思います。

しかし、Ectoに生のSQLを部分的または全体的にプレイさせる方法を見つけることはできません。それは、ectoがelixir dslのみをサポートし、dslが十分でない場合に生のSQLへのシェルアウトを許可しないことを意図していますか?

アダプタを介してクエリを実行できることがわかりました(Rocketはアプリの名前です)

q = Ecto.Adapters.Postgres.query(Rocket.Repo,"select * from users limit 1",[])

しかし、これをモデルにどのように組み込むかはわかりません。私はエリクサーを初めて使い、Ecto.Model.Schem .schema/3を使用できるはずですが、これは失敗します

Rocket.User.__schema__(:load,q.rows |> List.first,0)
** (FunctionClauseError) no function clause matching in Rocket.User.__schema__/3    
42
Krut

Postgresを使用したEcto 2.0(ベータ)では、Ecto.Adapters.SQL.query()current docs2.0-beta2 docs )を使用して任意のSQLを実行できます。行自体のリスト( "rows")に加えて、列名のリスト( "columns")を返すこともあります。

以下の例では、私は

  1. パラメータなしでカスタムクエリを実行し、
  2. 結果の列名を文字列からアトムに変換し、
  3. それらを結果の各行と組み合わせ、 Kernel.struct() で構造体にマッピングします

(おそらくquery()バージョンを実行し(バングなし!)、{ok, res}。)

qry = "SELECT * FROM users"
res = Ecto.Adapters.SQL.query!(Repo, qry, []) # a

cols = Enum.map res.columns, &(String.to_atom(&1)) # b

roles = Enum.map res.rows, fn(row) ->
  struct(MyApp.User, Enum.Zip(cols, row)) # c
end
33
jamesvl

Ecto 2.0の修正ソリューション:

repo.exで:

  def execute_and_load(sql, params, model) do
    Ecto.Adapters.SQL.query!(__MODULE__, sql, params)
    |> load_into(model)
  end

  defp load_into(response, model) do
    Enum.map(response.rows, fn row ->
      fields = Enum.reduce(Enum.Zip(response.columns, row), %{}, fn({key, value}, map) ->
        Map.put(map, key, value)
      end)
      Ecto.Schema.__load__(model, nil, nil, nil, fields,
                           &Ecto.Type.adapter_load(__adapter__, &1, &2))
    end)
  end

使用法:

Repo.execute_and_load("SELECT * FROM users WHERE id = $1", [1], User)
9
thousandsofthem

Ecto 1.0がリリースされたので、これはしばらくの間機能するはずです。

Repoモジュールに次の関数を追加します。

def execute_and_load(sql, params, model) do
  Ecto.Adapters.SQL.query!(__MODULE__, sql, params)
  |> load_into(model)
end

defp load_into(response, model) do
  Enum.map response.rows, fn(row) ->
    fields = Enum.reduce(Enum.Zip(response.columns, row), %{}, fn({key, value}, map) ->
      Map.put(map, key, value)
    end)

    Ecto.Schema.__load__(model, nil, nil, [], fields, &__MODULE__.__adapter__.load/2)
  end
end

そして、次のように使用します:

Repo.execute_and_load("SELECT * FROM users WHERE id = $1", [1], User)
7
Sean S

Ecto.Adapters.SQL.query/4 に加えて、 Ecto.Query.API.fragment/1 もあります。これは、クエリ式をデータベース。たとえば、Postgresの配列関数array_upper、使用するかもしれません

Ecto.Query.where([x], fragment("array_upper(some_array_field, 1)]" == 1)
6
licyeus

Ecto 2.2.8はEcto.Query.load/2ので、次のようなことができます。

use Ecto.Repo

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

https://hexdocs.pm/ecto/Ecto.Repo.html#c:load/2 を参照してください

5
eahanson

Ecto、少なくともバージョン〜> 0.7以降では使用する必要があります:

Ecto.Adapters.SQL.query/4

def query(repo, sql, params, opts \\ [])

指定されたリポジトリでカスタムSQLクエリを実行します。

成功した場合、少なくとも2つのキーを持つマップを含む:okタプルを返さなければなりません:

•:num_rows-影響を受ける行の数•:rows-リストとしての結果セット。コマンドが結果として行を生成しない場合、リストの代わりにnilが返される場合があります(ただし、返されない削除コマンドのように、影響を受ける行の数が生成されます)

オプション

•:timeout-コールの終了を待機する時間(ミリ秒)。:infinityは無期限に待機します(デフォルト:5000)•:log-falseの場合、クエリを記録しません

iex> Ecto.Adapters.SQL.query(MyRepo、 "SELECT $ 1 + $ 2"、[40、2])

%{rows:[{42}]、num_rows:1}

4
Peck

これは https://stackoverflow.com/users/1758892/thousandsofthem サンプルですが、少し縮小しました(クレジット:彼/彼女)

defmodule MyApp.Repo do
  [...]
  def execute_and_load(sql, params, schema) do
    response = query!(sql, params)
    Enum.map(response.rows, fn row ->
      fields = Enum.Zip(response.columns, row) |> Enum.into(%{})
      Ecto.Schema.__load__(schema, nil, nil, nil, fields,
        &Ecto.Type.adapter_load(__adapter__(), &1, &2))
    end)
  end
end
3
Michael Bishop