【问题标题】:INTERSECT multiple subqueries in MS-Access (without INTERSECT keyword)MS-Access 中的 INTERSECT 多个子查询(没有 INTERSECT 关键字)
【发布时间】:2014-10-14 04:32:05
【问题描述】:

我需要在 MS Access 中交叉多个子查询。不幸的是,Access-SQL 不支持 INTERSECT 关键字。我了解,我们如何使用两个表的 INNER JOIN 来获得我们想要的交集。但是我怎样才能以编程方式创建一个查询,使 N 个子查询相交呢?

更具体地说:我有一张员工表和一张专业化表。每个员工可以有多个专业化,这意味着员工和专业化之间存在多对多的关系,由一个包含员工和专业化 ID 的附加表表示,这并不复杂。

现在假设我想要一份员工列表,所有员工都在某处指定了所有专业。在任何其他 SQL 引擎中,我将简单地创建一个子查询列表并使用 INTERSECTION 关键字将这些子查询“连接”在一起,创建如下内容:

SELECT * FROM ( 
        (SELECT id, first_name, last_name FROM Employees JOIN Emp_spec 
            ON Employee.id = Emp_spec.spec_id WHERE Emp_spec.spec_id=x_1 )
        INTERSECT 
        ... 
        INTERSECT
        (SELECT id, first_name, last_name FROM Employees JOIN Emp_spec 
            ON Employee.id = Emp_spec.spec_id WHERE Emp_spec.spec_id=x_n )
);

,其中 x_1,...,x_n 表示对应于某些特化的一些 id。此查询返回一组员工,所有员工都具有所有特化 x_1,...,x_n。那么如何在没有 INTERSECT 关键字的 Access 中创建这样的查询。我一直在尝试使用 INNER JOIN 编写等效查询,但似乎无法成功。

【问题讨论】:

  • SELECT DISTINCT Employees.* FROM Employees INNER JOIN Emp_spec ON Employees.ID = Emp_spec.Employee_ID WHERE (((Emp_spec.Spec_ID) In (1,3)));
  • 假设您的链接表同时具有 [Employee_ID] 和 [Spec_ID]。它还假设 [Spec_id] 存储为数字而不是文本。如果是文本,则需要用单引号括起来。
  • 不,不是这样。您返回一组员工,其中每个员工至少有一个所需的专业。但是,我希望所有员工都拥有所有选定的专业。顺便说一句,您的选择与您在我上面的查询中使用 UNION 而不是 INTERSECT 执行相同的操作。
  • 啊,我不明白结果必须拥有所有选择的专业。 SELECT DISTINCT Employees.* FROM (Employees INNER JOIN Emp_spec ON Employees.ID = Emp_spec.Employee_ID) INNER JOIN Emp_spec AS Emp_spec_1 ON Employees.ID = Emp_spec_1.Employee_ID WHERE (((Emp_spec.Spec_ID)=1) AND ((Emp_spec_1.Spec_ID)=3)); 在本例中,我们过滤 1 和 3。在查询设计网格中可视化要容易得多。诀窍是多次引入 Emp_Spec 表。每个标准一份。每个人都通过 ID 加入到 Employee 表中。
  • 这些 cmets 正在参与其中。如果您要回答问题,则应发布答案而不是长评论。

标签: sql ms-access vba ms-jet-ace


【解决方案1】:

也许这就是你的想法。对于测试表 [Employees] ...

id  first_name  last_name
--  ----------  ---------
 1  Gord        Thompson 
 2  Homer       Simpson  
 3  Hank        Kingsley 

... 和 [Emp_spec] ...

emp_id  spec_id
------  -------
     1        1
     1        2
     2        1
     3        1
     3        2

...查询

SELECT * FROM Employees
WHERE id IN (SELECT emp_id FROM Emp_spec WHERE spec_id=1)
    AND id IN (SELECT emp_id FROM Emp_spec WHERE spec_id=2)

返回

id  first_name  last_name
--  ----------  ---------
 1  Gord        Thompson 
 3  Hank        Kingsley 

【讨论】:

  • 谢谢。这比我预期的要容易。不过有一点 - 我们需要使用 DISTNICT 来避免重复。
【解决方案2】:

如果你想用连接来做,你会为每个规范加入一个子查询,它只从 Emp_spec 中提取与单个 spec_id 匹配的行。请注意 Access 对多个联接的奇怪括号要求。

SELECT Employees.id, Employees.first_name
FROM (Employees
INNER JOIN
    (SELECT employee_id
    FROM Emp_spec
    WHERE spec_id = x_1) specx_1
ON Employees.id = specx_1.employee_id)
INNER JOIN
    (SELECT employee_id
    FROM Emp_spec
    WHERE spec_id = x_2) specx_2
ON Employees.id = specx_2.employee_id
...

不过,你可以走捷径,但你可能会认为它太笨拙了。

SELECT Employees.id, Employees.first_name
FROM Employees
INNER JOIN
     (SELECT employee_id
     FROM Emp_spec
     WHERE spec_id IN (x_1, x_2, x_3, ..., x_n)
     GROUP BY employee_id
     HAVING COUNT(*) = n) match_counts
ON Employees.id = match_counts.spec_id

这里的关键部分是HAVING COUNT(*) = n。当表被过滤为您想要的规范时,您只想获取在 Emp_spec 表中具有 n 记录的员工,其中 n 是您要过滤的规范数量。

【讨论】:

  • 有趣。我可以看到这里确实有足够的创造力空间。只是出于好奇,这里发布的三个工作解决方案中哪一个是最有效的? (在我的情况下这并不重要。)
  • 我会说我的第二个解决方案将是最有效的。使用 join 的方案和 Gord 的解决方案大致相当,除了有时 Access 不会很好地优化 WHERE 子句中的子查询,因此如果您遇到性能问题,您可以尝试使用 join 版本。
猜你喜欢
  • 2011-08-20
  • 2011-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-25
相关资源
最近更新 更多