Ecto子查询中的SQL WITH AS语句

时间:2018-01-09 20:40:09

标签: sql elixir common-table-expression ecto

我有一个SQL查询,使用PostgreSQL WITH AS充当XOR或“不”左连接。目标是返回两个查询之间唯一的内容。

在这种情况下,我想知道用户在特定时间段内有什么交易,并且在另一个时间段内没有交易。 SQL Query使用WITH选择new_transactions中特定日期范围的所有交易,然后选择older_transactions中其他日期范围的所有交易来执行此操作。在这些内容中,我们会在new_transactions中选择older_transactions NOT

我在SQL中的查询是:

/* New Customers */
WITH new_transactions AS (
       select * from transactions
       where merchant_id = 1 and inserted_at > date '2017-11-01'
     ), older_transactions AS (
        select * from transactions
        where merchant_id = 1 and inserted_at < date '2017-11-01'
     )
SELECT * from new_transactions
WHERE user_id NOT IN (select user_id from older_transactions);

我试图通过Subquery在Ecto中复制它。我知道我无法在subquery语句中执行where:,这会留下left_join。我如何在Elixir / Ecto中复制它?

我在Elixir / Ecto中复制的内容会引发(Protocol.UndefinedError) protocol Ecto.Queryable not implemented for [%Transaction....

Elixir / Ecto代码:

 def new_merchant_transactions_query(merchant_id, date) do
    from t in MyRewards.Transaction,
    where: t.merchant_id == ^merchant_id and fragment("?::date", t.inserted_at) >= ^date
  end

  def older_merchant_transactions_query(merchant_id, date) do
    from t in MyRewards.Transaction,
    where: t.merchant_id == ^merchant_id and fragment("?::date", t.inserted_at) <= ^date

  end

  def new_customers(merchant_id, date) do
    from t in subquery(new_merchant_transactions_query(merchant_id, date)),
    left_join: ot in subquery(older_merchant_transactions_query(merchant_id, date)),
    on: t.user_id == ot.user_id,
    where: t.user_id != ot.user_id,
    select: t.id
  end  

更新

我尝试将其更改为where: is_nil(ot.user_id),但收到同样的错误。

1 个答案:

答案 0 :(得分:0)

这可能应该是一个评论而不是一个答案,但它太长了,需要太多的格式,所以我继续发布这个作为答案。

除此之外,我要做的是重新编写查询以避免公用表表达式(或CTE;这是真正调用WITH AS)和IN()表达式,而不是我做一个实际的JOIN,像这样:

SELECT n.* 
FROM transactions n
LEFT JOIN transactions o ON o.user_id = n.user_id and o.merchant_id = 1 and o.inserted_at < date '2017-11-01'
WHERE n.merchant_id = 1 and n.inserted_at > date '2017-11-01'
    AND o.inserted_at IS NULL

您也可以选择NOT EXISTS(),这在Sql Server上至少可以产生更好的执行计划。

这可能是处理查询的更好方法,但是一旦你这样做,你也可能会发现这可以通过更容易翻译成ecto来解决你的问题。