【问题标题】:selecting records from parent / child tables从父/子表中选择记录
【发布时间】:2015-05-15 18:31:58
【问题描述】:

我有如下所示的父/子表:

CREATE TABLE users(
  id integer primary key AUTOINCREMENT,
  pnum varchar(10),
  dloc varchar(100),
  cc varchar(10),
  name varchar(255),
  group active bit(1)
);

CREATE TABLE group_members(
  id integer primary key AUTOINCREMENT,
  group_id integer,
  member_id integer,
  FOREIGN KEY (group_id) REFERENCES users(id),
  FOREIGN KEY (member_id) REFERENCES users(id)
);

用户数据如下:

ID  PNUM  DLOC          CC   NAME      GRP
 86|23101|dloc 89|    |          |0
 87|23101|dloc 90|    |          |0
 88|23102|dloc 91|    |          |0
590|12345|Group  |    |Test Group|1
591|90000|dloc 1 |    |          |0

group_members 数据如下:

ID GROUP_ID   
1  |590    | 87
2  |590    | 88

基于 PNUM,我希望能够获取所有用户的 dloc 值,无论它是否是一个组。

例如,如果有人请求 pnum 23101,我想返回

  • “dloc 89”和
  • “dloc 90”

但如果他们要求 12345,我想回来

  • “dloc 90”和
  • “dloc 91”

到目前为止,我想出了这个查询:

SELECT users.dloc 
FROM users 
WHERE users.id IN 
     (SELECT group_members.member_id
      FROM group_members 
      INNER JOIN users on users.id = group_members.group_id 
      WHERE users.pnum='12345');

这适用于组,但如果我使用 pnum 23101 运行相同的查询,它不会返回任何结果。

到目前为止我所做的尝试

我试着看看我是否可以像这样使用 OR:

 SELECT users.dloc 
 FROM users 
 WHERE users.id in 
       (SELECT group_members.member_id 
        FROM group_members 
        INNER JOIN users on users.id = group_members.group_id 
        WHERE users.pnum='12345');
   OR
       (SELECT users.dloc 
        FROM users 
        WHERE pnum='12345')

任何建议将不胜感激。

【问题讨论】:

  • 如果有人请求 pnum 23101,我想取回 "dloc 89" 和 "dloc 90" 但如果他们请求 12345,我想取回 "dloc 89", " dloc 90" 和 "dloc 99" 这是正确的,还是你把它颠倒过来了?该组 (12345) 只有 2 个成员,而 pnum=23101 有 3 个匹配行。
  • @MichaelBerkowski 我已经更新了这个问题。对不起,我确实有一些错别字。希望现在更清楚了。

标签: sql sqlite join


【解决方案1】:

您可以使用UNION 来执行两个 查询,其中一个直接在PNUM 上匹配GROUP <> 1 的值,另一个在PNUM 上匹配并通过@ 连接987654327@ 用于 GROUP = 1 的值。

第二部分,需要两次加入users,才能得到匹配原PNUM的群组成员。

由于GROUP 的条件在每个条件中都是相反的,所以UNION 中只有一部分会返回结果。

/* First part of UNION directly matches PNUM for GROUP = 0 */
SELECT dloc
FROM users
WHERE 
  PNUM = 23101
  AND `group` <> 1
UNION
/**
 Second part of UNION matches GROUP = 1
 and joins through group_members back to users
 to get member dloc (from the second users join)
 */
SELECT uu.dloc
FROM users u
   INNER JOIN group_members m ON u.`GROUP` = 1 AND u.id = m.group_id
   INNER JOIN users uu ON m.member_id = uu.id
WHERE 
   u.PNUM = 23101

不幸的是,这确实需要在查询中放置两次 PNUM 值,每个 UNION 部分一次,但这还不错。

Here it is in action(使用 MySQL 而不是 SQLite,但这并不重要)

使用带有ORIN() 子查询的原始方法也可以完成,但我为GROUP = 0GROUP = 1 添加了WHERE 条件。

SELECT users.dloc 
FROM users 
WHERE users.id in (
    SELECT group_members.member_id 
    FROM group_members 
      INNER JOIN users on users.id = group_members.group_id 
    WHERE users.pnum='23101' AND `GROUP` = 1
 )
 OR users.id IN (
    SELECT users.ID
    FROM users 
    WHERE pnum='23101' AND `GROUP` = 0
);

And here's the alternative method in action...

【讨论】:

  • @dot 也可以按照我刚刚添加的原始方式完成。
  • 你认为哪种方式更高效
  • @dot 我没有在 MySQL 中对它们运行 EXPLAIN(而且 SQLite 可能无论如何都会表现不同),但如果我不得不猜测,我敢打赌它们的表现完全相同。它们实际上是完全相同的两个查询,一个只是用UNION 打包,另一个打包为子查询。 UNION 没有外部 IN() 条件来获取原始用户,因此它可能会更快,但我会怀疑很多,特别是如果 users.id 被索引。
  • 实际上,我还没有在这个 sqlite 数据库中建立任何索引,因为我想找到一篇文章讨论在 sqlite 中为 CRUD 操作索引是否真的有帮助。
  • id 已经是PRIMARY KEY,所以它已经被隐式索引了。我在上一条评论中忘了考虑这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多