在 JOIN 之外引用 SELECT

Refer to SELECT outside of JOIN

我在 my previous question 中得到了一些很大的帮助。这个建立在那个之上。我想检索连接查询的信息,但我不知道在 SQL.

中是否可行

这是一个包含平民的虚拟数据库。每行包含他们的姓名、城市、街道、街道号码和工作。在 SQLite:

中生成数据库的最小示例
import sqlite3
from pathlib import Path


def main():
    build_cmd = """CREATE TABLE civs (
    name TEXT,
    city TEXT,
    street TEXT,
    snumber INTEGER,
    job TEXT) """

    pdb = Path("dummy.db")
    if pdb.exists():
        pdb.unlink()
    conn = sqlite3.connect(str("dummy.db"))

    cur = conn.cursor()
    cur.execute(build_cmd)

    insert_cmds = """
    BEGIN TRANSACTION;
    INSERT INTO civs VALUES ('john', 'dublin', 'butchers_street', '42', 'butcher');
    INSERT INTO civs VALUES ('marie', 'london', 'butchers_street', '3', 'baker');
    INSERT INTO civs VALUES ('keith', 'london', 'camel_road', '7', 'carpenter');
    INSERT INTO civs VALUES ('anna', 'dublin', 'butchers_street', '9', 'butcher');
    INSERT INTO civs VALUES ('steve', 'london', 'maceys_alley', '1', 'blacksmith');
    INSERT INTO civs VALUES ('beth', 'dublin', 'butchers_street', '68', 'grocer');
    INSERT INTO civs VALUES ('lisa', 'dublin', 'church_street', '32', 'nun');
    COMMIT;
    """

    cur.executescript(insert_cmds)
    cur.close()
    conn.commit()
    conn.close()

if __name__ == '__main__':
    main()

假设我想检索一行(所以住在约翰街的每个人一行)。我使用“一个”约翰,因为理论上你当然可以有多个约翰。

到此为止,它检索了主查询的信息。

SELECT
    civs.street, civs.city, civs.name, civs.job
FROM civs
JOIN
    (SELECT city, street
     FROM civs
     WHERE name='john') jcivs

    ON civs.street = jcivs.street
        AND civs.city = jcivs.city

这给出了

street city name job
butchers_street dublin anna butcher
butchers_street dublin beth grocer
butchers_street dublin john butcher

但是“John 的工作”严重缺失 并且 我将 john 作为单独的一行,我不想要它,因为我们对 John 的邻居感兴趣。所以我不确定如何从最外层 SELECT 引用 jcivs。所以这不起作用,因为 jcivs 对于外部 SELECT:

不存在
SELECT
    civs.street, civs.city, jcivs.job, civs.name, civs.job

预期结果:

street city johnsjob name job
butchers_street dublin butcher anna butcher
butchers_street dublin butcher beth grocer

注意:效率对我来说很重要。您可以假设它是冷的 table 并且所有列都已编入索引。

这应该是一种方法:

WITH civs2 AS (
  SELECT civs.*, row_number() OVER () as id FROM civs
 )
 SELECT
    civs2.street, civs2.city, jcivs.job, civs2.name, civs2.job
FROM civs2
JOIN
    (SELECT city, street, job, id
     FROM civs2
     WHERE name='john') jcivs

    ON civs2.street = jcivs.street
        AND civs2.city = jcivs.city
        AND civs2.id <> jcivs.id

根据您的查询,我们:

  1. 将作业添加到 jcivs 子查询;和
  2. 通过创建一个带有基于行号的 ID 的 CTE 来演示 ID 的使用。然后,我们确保 civs.id <> jcivs.id

如果您有 table 的 ID,那可能会更有效,因为我们可以将其添加到连接 civs.id <> jcivs.id

不需要创建 id 列,因为 SQLite 为每个 table 提供了一个 rowid 列(这是 table 的实际主键) .

像这样做一个简单的自连接:

SELECT c1.*
FROM civs c1 INNER JOIN civs c2
ON (c2.city, c2.street) = (c1.city, c1.street) AND c2.rowid <> c1.rowid
WHERE c2.name = 'john';

参见demo