Ecto 子查询中的 SQL WITH AS 语句

Posted

技术标签:

【中文标题】Ecto 子查询中的 SQL WITH AS 语句【英文标题】:SQL WITH AS statements in Ecto Subquery 【发布时间】:2018-01-09 20:40:08 【问题描述】:

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

在这种情况下,我想知道哪些用户在某个时间段内有交易,而在另一个时间段内没有交易。 SQL 查询通过使用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);

我正在尝试通过子查询在 Ecto 中复制它。我知道我不能在where: 语句中使用subquery,这会留下left_join。如何在 Elixir/Ecto 中复制它?

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

灵药/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),,但得到了同样的错误。

【问题讨论】:

LEFT JOIN &lt;some_table&gt; ON &lt;related_keys&gt; WHERE &lt;some_table.column&gt; IS NULL。不过,我不知道该怎么写。请注意,您当前的最终 WHERE 似乎是将您的 LEFT OUTER JOIN 变成(常规)INNER JOIN 的条件。 我尝试将其更改为 where: is_nil(ot.user_id), 并得到相同的错误。可能是别的东西 请包含完整的错误,看起来好像已经从 Repo 中读取了交易列表?? 完全错误:gist.github.com/cdesch/8defe0d38b69c7ce905cf534cd6ece0e 请附上要点中使用的new_merchant_transactions 代码的来源。 【参考方案1】:

这可能应该是评论而不是答案,但它太长并且需要太多格式,所以我继续并将其发布为答案。有了这个,我们开始吧。

我要做的是重新编写查询以避免公用表表达式(或 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。

【讨论】:

以上是关于Ecto 子查询中的 SQL WITH AS 语句的主要内容,如果未能解决你的问题,请参考以下文章

SQL 语句递归查询 With AS 查找所有子节点

sql优化之 with as

使用 with as 优化SQL

Oracle With As 查询

with as 用法

Oracle中with as的用法 zf