简体   繁体   English

如何在 PostgreSQL 中加入两个“未命名的表/选择”?

[英]How can I join two “unnamed tables/selections” in PostgreSQL?

Given the tables person and car and a relationship based on car.id between the two tables, I could join them using the following code:给定表格personcar以及两个表格之间基于car.id的关系,我可以使用以下代码加入它们:

SELECT * FROM person
JOIN car
ON person.car_id = car.id;

However, I fail to figure out how to do this if I am working with two unnamed selections.但是,如果我正在使用两个未命名的选择,我无法弄清楚如何做到这一点。 Specifically, I would like to join the below selections based on id .具体来说,我想加入以下基于id的选择。

SELECT id, phone_number
FROM student
WHERE id IN
    (SELECT account_id
    FROM loan
    WHERE id IN
        (SELECT loan_id
        FROM fine
        WHERE paid_amount < fine_amount))
UNION
SELECT id, phone_number
FROM administrator
WHERE id IN
    (SELECT account_id
    FROM loan
    WHERE id IN
        (SELECT loan_id
        FROM fine
        WHERE paid_amount < fine_amount))
SELECT
    account_id,
    EXTRACT ('day' FROM (NOW()::timestamp - expiry_date::timestamp))
FROM loan
WHERE id IN
    (SELECT loan_id
    FROM fine
    WHERE paid_amount < fine_amount)

To clarify, if the first selection would be called table1 and the second selection would be called table2 , I would like to join the selections according to the below code为了澄清,如果第一个选择被称为table1而第二个选择被称为table2 ,我想根据下面的代码加入选择

SELECT * FROM table1
JOIN table2
ON table1.id = table2.account_id;

You can give them a name each by putting the queries inline:您可以通过将查询内联来给它们一个名称:


SELECT * FROM
(

--begin your first query
  SELECT id, phone_number
  FROM student
  WHERE id IN
    (SELECT account_id
    FROM loan
    WHERE id IN
        (SELECT loan_id
        FROM fine
        WHERE paid_amount < fine_amount))
  UNION
  SELECT id, phone_number
  FROM administrator
  WHERE id IN
    (SELECT account_id
    FROM loan
    WHERE id IN
        (SELECT loan_id
        FROM fine
        WHERE paid_amount < fine_amount))
--end your first query

) t1
INNER JOIN
(

--begin your second query
  SELECT
    account_id,
    EXTRACT ('day' FROM (NOW()::timestamp - expiry_date::timestamp))
  FROM loan
  WHERE id IN
    (SELECT loan_id
    FROM fine
    WHERE paid_amount < fine_amount)
--end your second query


) t2
ON
t1.id = t2.account_id

You can also turn each query into a named CTE:您还可以将每个查询转换为命名的 CTE:

WITH table1 AS (

  SELECT id, phone_number
  FROM student
  WHERE id IN
    (SELECT account_id
    FROM loan
    WHERE id IN
        (SELECT loan_id
        FROM fine
        WHERE paid_amount < fine_amount))
  UNION
  SELECT id, phone_number
  FROM administrator
  WHERE id IN
    (SELECT account_id
    FROM loan
    WHERE id IN
        (SELECT loan_id
        FROM fine
        WHERE paid_amount < fine_amount))
),

table2 as (
  SELECT
    account_id,
    EXTRACT ('day' FROM (NOW()::timestamp - expiry_date::timestamp))
  FROM loan
  WHERE id IN
    (SELECT loan_id
    FROM fine
    WHERE paid_amount < fine_amount)
)

--begin the query that joins the CTEs
SELECT * FROM table1
JOIN table2
ON table1.id = table2.account_id;

I'm struck though that you could simplify this query, something like this:尽管您可以简化此查询,但我感到很震惊,如下所示:

SELECT
  people.*,
  EXTRACT ('day' FROM (NOW()::timestamp - l.expiry_date::timestamp))
FROM
(
  SELECT 'student' as typ, id, phone_number FROM student
  UNION ALL
  SELECT 'admin', id, phone_number FROM administrator
) people

INNER JOIN loan l ON people.id = l.account_id
INNER JOIN fine f ON l.id = f.loan_id
WHERE f.paid_amount < f.fine_amount

You should get into the habit of aliasing everything you put in a query (unless it's a subquery that filters one table only) and then use the aliases:您应该养成为查询中的所有内容设置别名的习惯(除非它是仅过滤一个表的子查询),然后使用别名:

SELECT s.id as studentid, l.id as loanid FROM
  student s
  INNER JOIN loan l ON s.id = l.account_id

The reasons are many, but by using an alias and fully qualifying all your column references you prevent your query from breaking if someone adds a new column to one of the tables in future, where the name clashes with an existing column.原因有很多,但是通过使用别名并完全限定所有列引用,如果将来有人向其中一个表中添加新列(其中名称与现有列发生冲突),您可以防止查询中断。 By aliasing tables it allows you to join tables in multiple times.通过给表起别名,它允许您多次连接表。 Example a student has a term_address and a home address:例如一个学生有一个 term_address 和一个家庭地址:

SELECT * FROM
  student s
  INNER JOIN address ahome on s.home_address_id = ahome.id
  INNER JOIN address aterm on s.term_address_id = aterm.id

This way you don't need a table for term addresses and a table for home addresses - the address table stores all addresses and you alias it differently.这样,您就不需要一个学期地址表和一个家庭地址表 - 地址表存储所有地址并且您使用不同的别名。 A different subset of records participates in each join不同的记录子集参与每个连接

Anything that represents a block of data can be aliased, be it a table or a subquery, or possibly some other things.任何表示数据块的东西都可以是别名,可以是表或子查询,也可能是其他一些东西。

SELECT * FROM
  (subquery here) aliasForSubquery
  INNER JOIN
  (anther subquery) aliasForANotherSubquery
  ON
    aliasForSubquery.column = aliasForAnotherSubquery.column

The only thing you don't need to(can't) alias is the subquery that creates your IN(...) list您唯一不需要(不能)别名的是创建IN(...)列表的子查询

You can use cte s:您可以使用cte s:

with 
cte1 as (
SELECT id, phone_number
FROM student
WHERE id IN
    (SELECT account_id
    FROM loan
    WHERE id IN
        (SELECT loan_id
        FROM fine
        WHERE paid_amount < fine_amount))
UNION
SELECT id, phone_number
FROM administrator
WHERE id IN
    (SELECT account_id
    FROM loan
    WHERE id IN
        (SELECT loan_id
        FROM fine
        WHERE paid_amount < fine_amount)
),
cte2 as (
SELECT
    account_id,
    EXTRACT ('day' FROM (NOW()::timestamp - expiry_date::timestamp))
FROM loan
WHERE id IN
    (SELECT loan_id
    FROM fine
    WHERE paid_amount < fine_amount)
)
SELECT * 
FROM cte1 JOIN cte2
ON cte1.id = cte2.account_id;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM