SQL Ecto 子查询中的 WITH AS 语句
SQL WITH AS statements in Ecto Subquery
我有一个 SQL 查询,它使用 PostgreSQL WITH AS
作为 XOR
或 "Not" 左连接。目标是 return 两个查询之间的唯一性。
在这种情况下,我想知道哪些用户在某个时间段内有交易,而在另一个时间段内没有交易。 SQL 查询通过使用 WITH
到 select 在 new_transactions
中特定日期范围内的所有交易,然后 select 在另一个日期范围内的所有交易来做到这一点older_transactions
。从那些,我们将 select 从 new_transactions
什么是 NOT in older_transactions
.
我在 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....
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),
但得到了同样的错误。
这也许应该是评论而不是答案,但它太长并且需要太多的格式,所以我继续发布它作为答案。有了这个,我们开始吧。
我要做的是重写查询以避免 Common Table 表达式(或 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 服务器上至少通常会产生更好的执行计划。
无论如何,这可能是处理查询的更好方法,但是一旦您这样做了,您可能还会发现这解决了您的问题,因为它更容易转换为 ecto。
我有一个 SQL 查询,它使用 PostgreSQL WITH AS
作为 XOR
或 "Not" 左连接。目标是 return 两个查询之间的唯一性。
在这种情况下,我想知道哪些用户在某个时间段内有交易,而在另一个时间段内没有交易。 SQL 查询通过使用 WITH
到 select 在 new_transactions
中特定日期范围内的所有交易,然后 select 在另一个日期范围内的所有交易来做到这一点older_transactions
。从那些,我们将 select 从 new_transactions
什么是 NOT in older_transactions
.
我在 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....
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),
但得到了同样的错误。
这也许应该是评论而不是答案,但它太长并且需要太多的格式,所以我继续发布它作为答案。有了这个,我们开始吧。
我要做的是重写查询以避免 Common Table 表达式(或 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 服务器上至少通常会产生更好的执行计划。
无论如何,这可能是处理查询的更好方法,但是一旦您这样做了,您可能还会发现这解决了您的问题,因为它更容易转换为 ecto。