sql避免笛卡尔积

时间:2013-01-26 19:32:24

标签: sql

我对SQL很陌生,并且正在努力查询(使用Access,FWIW)。我用Google搜索并搜索了StackOverflow,但我还没有看到这个确切的场景。 (这也可能是因为我不知道正确的搜索条件。)

我有两个非常简单的表,包含类似的数据。

table1: state, lname, fname, network
table2: state, lname, fname, network

我想要的是找到两个表中匹配的每个人/州组合以及该人所在的每个表中的网络:

state, lname, fname, t1.network, t2.network.  

此人可能位于每个表格中的多个网络中。我希望看到该人所属的每个网络(来自两个表)。

我开始使用如下的JOIN:

SELECT t1.state, t1.lname, t1.fname, t1.network, t2.network
FROM t1 INNER JOIN t2 
ON t1.fname=t2.fname AND t1.lname=t2.lname AND t1.state=t2.state
GROUP BY t1.state, t1.lname, t1.fname, t1.network, t2.network

我很快发现我得到了笛卡尔积。因此,如果“NY,Smith,John”在t1的两个网络和t2的三个网络中,我会得到这样的结果:

NY, Smith, John, NetworkA, NetworkB
NY, Smith, John, NetworkA, NetworkA
NY, Smith, John, NetworkB, NetworkA
NY, Smith, John, NetworkB, NetworkB
NY, Smith, John, NetworkA, NetworkC
NY, Smith, John, NetworkB, NetworkC

我真正想看到的只是:

NY, Smith, John, NetworkA, NetworkA
NY, Smith, John, NetworkB, NetworkB
NY, Smith, John, NULL, NetworkC

任何人都可以就如何继续或指出正确的方向给我一些建议吗?

5 个答案:

答案 0 :(得分:1)

所以看起来你希望每个表中的所有记录都是相同的,然后只有每个记录中的记录都是不同的。这意味着您需要UNION 3组查询。

尝试这样的事情:

SELECT t1.state, 
   t1.lname, 
   t1.fname, 
   t1.network as t1Network, 
   t2.network as t2Network
FROM table1 t1 
   INNER JOIN table2 t2 
      ON t1.fname=t2.fname 
      AND t1.lname=t2.lname 
      AND t1.state=t2.state
      AND t1.network=t2.network
UNION 
SELECT t1.state, 
   t1.lname, 
   t1.fname, 
   t1.network as t1Network, 
   t2.network as t2Network
FROM table1 t1 
   LEFT JOIN table2 t2 
      ON t1.fname=t2.fname 
      AND t1.lname=t2.lname 
      AND t1.state=t2.state
      AND t1.network=t2.network
WHERE t2.network IS NULL
UNION 
SELECT t2.state, 
   t2.lname, 
   t2.fname, 
   t1.network as t1Network, 
   t2.network as t2Network
FROM table2 t2 
   LEFT JOIN table1 t1
      ON t1.fname=t2.fname 
      AND t1.lname=t2.lname 
      AND t1.state=t2.state
      AND t1.network=t2.network
WHERE t1.network IS NULL

这可以为您提供所需的结果。

以下是要确认的SQL Fiddle

- 编辑

今天没想到 - 你真的不需要第一次查询。您可以从第二个查询中删除WHERE条件,它的工作方式相同。累了: - )

这是更新的查询 - 两者都应该可以正常工作,这更容易阅读:

SELECT t1.state, 
   t1.lname, 
   t1.fname, 
   t1.network as t1Network, 
   t2.network as t2Network
FROM table1 t1 
   LEFT JOIN table2 t2 
      ON t1.fname=t2.fname 
      AND t1.lname=t2.lname 
      AND t1.state=t2.state
      AND t1.network=t2.network
UNION 
SELECT t2.state, 
   t2.lname, 
   t2.fname, 
   t1.network as t1Network, 
   t2.network as t2Network
FROM table2 t2 
   LEFT JOIN table1 t1
      ON t1.fname=t2.fname 
      AND t1.lname=t2.lname 
      AND t1.state=t2.state
      AND t1.network=t2.network
WHERE t1.network IS NULL

updated fiddle

顺便说一句 - 这些都应该在MSAccess中起作用,因为它支持UNION

祝你好运。

答案 1 :(得分:0)

SELECT DISTINCT t1.state, t1.lname, t1.fname, t1.network, t2.network
FROM t1 INNER JOIN t2 
ON t1.fname=t2.fname AND t1.lname=t2.lname AND t1.state=t2.state
GROUP BY t1.state, t1.lname, t1.fname, t1.network, t2.network

你得到了你想要的东西。

几乎每个联接都开始构建一个笛卡尔积(参见Inside SQL Server:查询书)。当您尝试过滤掉数据时,通常,创建的虚拟表将在您使用distinct时返回一个不同的集合。实际上,没有阻止笛卡尔积。

答案 2 :(得分:0)

在这种情况下,UNION应该是你最好的选择:

SELECT * FROM t1
UNION
SELECT * FROM t2

(请注意,如果两个表中的所有列的类型相同,顺序相同,则此查询应该按原样运行 - 否则最好指定每个必需的列,而不是使用SELECT *)。

答案 3 :(得分:0)

你想要的是一个UNION。

我将创建一个查询(在SQL视图中),效果为:

SELECT state, lname, fname, network, network
FROM t1
UNION ALL
SELECT state, lname, fname, network, network
FROM t2

然后我会创建其他查询该查询的查询。

我自己尝试给你提供更多详细信息,但我没有安装MS Access。

答案 4 :(得分:0)

一种选择是使用完全外连接:

SELECT coalesce(t1.state, t2.state), coalesce(t1.lname, t2.lname), coalesce(t1.fname, t2.fname),
  t1.network, t2.network
FROM t1 FULL OUTER JOIN t2 
ON t1.fname=t2.fname AND t1.lname=t2.lname AND t1.state=t2.state
and t1.network = t2.network

输出:

NY  SMITH   JOHN    A   A
NY  SMITH   JOHN    B   B
NY  SMITH   JOHN    NULL    C  

编辑:我没有看到你正在使用Access - 这是标准的SQL,但我不知道它是否会在那里工作。