带有嵌套 SQL 查询或如何查找最后一个 INET 的 Postgres 视图

Posted

技术标签:

【中文标题】带有嵌套 SQL 查询或如何查找最后一个 INET 的 Postgres 视图【英文标题】:Postgres view with nested SQL query or how to find the last INET 【发布时间】:2016-06-02 09:18:32 【问题描述】:

我有一个这样的查询:

SELECT DISTINCT(orders.email), uuid_nil() AS customer_id, 'Order' AS customer_type, orders.first_name, orders.last_name, MAX(orders.paid_at) AS last_order_at, 1 AS order_count, SUM(orders.total_price_cents) AS total_spent_pennies
FROM orders
WHERE orders.state = 'paid' AND orders.customer_id IS null
GROUP BY orders.email, customer_id, orders.first_name, orders.last_name
UNION
SELECT DISTINCT(customers.email), customers.id AS customer_id, 'Customer' AS customer_type, customers.first_name, customers.last_name, MAX(orders.paid_at) AS last_order_at, COUNT(orders.*) AS order_count, SUM(orders.total_price_cents) AS total_spent_pennies
FROM customers
JOIN orders ON customers.id = orders.customer_id
GROUP BY customers.email, customers.id, customers.first_name, customers.last_name

看起来像:

+-------------------------------+--------------------------------------+---------------+------------+--------------+-------------------------+-------------+---------------------+
| email                         | customer_id                          | customer_type | first_name | last_name    | last_order_at           | order_count | total_spent_pennies |
+-------------------------------+--------------------------------------+---------------+------------+--------------+-------------------------+-------------+---------------------+
| blah@gmail.com                | 00000000-0000-0000-0000-000000000000 | Order         | Richard    | Doe          | 2015-12-18 14:45:22 UTC | 1           | 2000                |
| paul@blah.com                 | 00000000-0000-0000-0000-000000000000 | Order         | Paul       | Doe          | 2016-04-05 09:04:57 UTC | 1           | 5000                |
+-------------------------------+--------------------------------------+---------------+------------+--------------+-------------------------+-------------+---------------------+

我的问题是如何将他们的最后一个 IP 地址也包括在内(INET 列)。一个日期我可以简单地使用 MAX 聚合函数,但 IP 地址显然没有。

基本上,我怎样才能结合上面的这个查询给我一个新的列,其中包含他们的 last_ip 地址,例如:

SELECT browser_ip FROM orders
WHERE email = 'blah@gmail.com'
ORDER BY paid_at DESC
LIMIT 1

【问题讨论】:

【参考方案1】:

您可能可以像这样使用简单的子查询,您只需要命名您的查询以便在它们之间进行引用。

http://www.techonthenet.com/postgresql/subqueries.php

例如,查询的第一部分类似于:

SELECT DISTINCT(c1.email), c1.id AS customer_id, 'Customer' AS customer_type, 
c1.first_name, c1.last_name, MAX(orders.paid_at) AS last_order_at, 
COUNT(orders.*) AS order_count, SUM(orders.total_price_cents) AS total_spent_pennies, 
(SELECT browser_ip FROM orders WHERE c1.email = orders.email 
ORDER BY paid_at DESC LIMIT 1) last_ip
FROM customers c1
JOIN orders ON c1.id = orders.customer_id
GROUP BY c1.email, c1.id, c1.first_name, c1.last_name

【讨论】:

【参考方案2】:

Cast to varchar,并使用string_agg - 类似:

SELECT email, paid_at, string_agg(browser_ip::varchar, ',') as ips 
WHERE email = 'blah@gmail.com'
GROUP BY email, paid_at
ORDER BY email, paid_at DESC
LIMIT 1

应该可以正常工作。

【讨论】:

【参考方案3】:

几个选项:

    使用 LATERAL 子查询。请注意,这将强制进行嵌套循环连接。 编写一个函数来检索最新的 IP 地址并调用它。它还会强制执行嵌套循环。 使用窗口函数和过滤器。这通常会执行得更糟,因为您必须在加入之前扫描整个表。

在你的情况下,由于工会,我可能会做第二个并做这样的事情:

CREATE OR REPLACE FUNCTION latest_ip(in_email text)
RETURNS inet LANGUAGE SQL AS
$$
SELECT paid_at, string_agg(browser_ip::varchar, ',') as ips 
WHERE email = in_email
GROUP BY paid_at
ORDER BY paid_at DESC
LIMIT 1
$$;

那么您只需在列列表中调用latest_ip(orders.email)

另一个需要在联合的两个分支上的 LATERAL 语句之后将上述内容复制为子查询。值得了解,但在这种情况下可能是维护问题。

【讨论】:

以上是关于带有嵌套 SQL 查询或如何查找最后一个 INET 的 Postgres 视图的主要内容,如果未能解决你的问题,请参考以下文章

查找 SQL 聚合函数调用中的百分比可能没有嵌套聚合或窗口函数

带有 unnest 的嵌套 SQL 评估问题

MongoDB查询:如何查找嵌套对象中的字符串

如何通过参数(SQL)查找最后一项? [复制]

带有连接的 SQL 查询以获取嵌套的对象数组

高效的 SQL 查询或索引来查找所有列是不是只有 1 个值