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

带嵌套SQL查询的Postgres视图或如何查找最后一个INET,sql,postgresql,postgresql-9.4,sql-view,Sql,Postgresql,Postgresql 9.4,Sql View,我有这样一个问题: 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 order

我有这样一个问题:

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列)。一个日期我可以简单地使用最大聚合函数,但IP地址显然没有

基本上,我如何结合上面的查询,为我提供一个新列,其中包含他们的最后一个ip地址,如下所示:

SELECT browser_ip FROM orders
WHERE email = 'blah@gmail.com'
ORDER BY paid_at DESC
LIMIT 1
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
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
,并使用-类似于:

SELECT browser_ip FROM orders
WHERE email = 'blah@gmail.com'
ORDER BY paid_at DESC
LIMIT 1
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
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
应该可以正常工作。

有几个选项:

  • 使用横向子查询。注意,这将强制嵌套循环联接
  • 编写一个函数来检索最新的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\u ip(orders.email)


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

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

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

    SELECT browser_ip FROM orders
    WHERE email = 'blah@gmail.com'
    ORDER BY paid_at DESC
    LIMIT 1
    
    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
    
    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