有没有办法从2个表中获取数据而不创建笛卡尔积?

时间:2011-12-19 22:56:19

标签: sql sql-server tsql

在我们的数据库中,客户可以拥有任意数量的司机,任意数量的车辆,任意数量的存储位置,这些位置的任意数量的建筑物,任意数量的评论等等。我需要一个返回所有客户信息的查询,现在查询类似于:

SELECT *
FROM Customer c
INNER JOIN Driver d ON c.ID = d.CustomerID
INNER JOIN Vehicle v ON c.ID = v.CustomerID

客户越多,结果越大,并且由于在此处创建了笛卡尔积,因此它呈指数级增长。 3个驱动程序,3个vechiles创建9行,与我们的真实数据相比,这是一个非常小的例子。实际上,我们有10个不同的表,可以根据需要为每个客户保留尽可能多的行。每个客户每个表至少有2-7行标准。我们已经返回了多达60,000,000多行(在10个不同的表中各有6个项目,6 ^ 10 = 60,466,176),对于我们的目的,如果我们只能 坚持 每个表中的6行。

所以在较小的例子中,如果1个客户有2辆车和3个司机而另一个客户有2辆车和1个司机我想要一个看起来像的结果集:

CustomerID  | DriverID | VehicleID
1           | 1        | 1
1 (or NULL) | 2        | 2
1 (or NULL) | NULL     | 3
2           | 3        | 4
2 (or NULL) | 4        | NULL

相反,我们在CustomerID上将每个表连接在一起的查询如下所示:

CustomerID | DriverID | VehicleID
1          | 1        | 1
1          | 1        | 2
1          | 1        | 3
1          | 2        | 1
1          | 2        | 2
1          | 2        | 3
2          | 3        | 4
2          | 4        | 4

真的,我想做的只是:

SELECT * FROM Driver
SELECT * FROM Vehicle

因为我们对数据的所有操作都循环遍历行并格式化文档中的信息。列出所有车手,然后列出所有车辆。在我们不必要的情况下进行这种疯狂的巨大连接是没有意义的,但它只是一个任意的要求,它必须返回一个拒绝倾听理性的顽固上级的结果集中的所有数据。由于列不同,因此不可能使用UNION。我只是希望有一种方法可以将它们水平而不是垂直地粘在一起。

另外,我正在使用Microsoft SQL Server。

2 个答案:

答案 0 :(得分:3)

这是一个丑陋的黑客,但你知道你正确的解决方案正如你所说:

SELECT * FROM Driver
SELECT * FROM Vehicle

相反,您可以使用联合查询并清除其他表中的列,只需使用设置列的类型和名称的查询启动它,并使用false coldition,因此它不会返回行:< / p>

SELECT 1 AS DriverID, "" AS DriverName, 1 AS VehicleID, "" AS VehicleName WHERE 1=0 
UNION SELECT DriverID, DriverName, NULL, NULL FROM Driver
UNION SELECT NULL, NULL, VehicleID, VehicleName FROM Driver

真的,真的很糟糕的代码!继续为你的上司工作以提供更好的解决方案。

答案 1 :(得分:1)

以下是我的表现方式。而不是:

SELECT *
FROM Customer c
INNER JOIN Driver d ON c.ID = d.CustomerID
INNER JOIN Vehicle v ON c.ID = v.CustomerID

我在做:

WITH CustomerCTE AS
(
  SELECT 1 ROW_NUM, ID
  FROM Customer
),
DriverCTE AS
(
  SELECT ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY ID) ROW_NUM, *
  FROM Driver
),
VehicleCTE AS
(
  SELECT ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY ID) ROW_NUM, *
  FROM Vehicle
)
SELECT *
FROM CustomerCTE c
FULL OUTER JOIN DriverCTE d ON c.ID = d.CustomerID AND c.ROW_NUM = d.ROW_NUM
FULL OUTER JOIN VehicleCTE v ON d.CustomerID = v.CustomerID AND d.ROW_NUM = v.ROW_NUM
ORDER BY
CASE WHEN c.ID IS NOT NULL THEN c.ID ELSE
  CASE WHEN d.CustomerID IS NOT NULL THEN d.CustomerID ELSE
    v.CustomerID
  END
END,
CASE WHEN c.ROW_NUM IS NOT NULL THEN c.ROW_NUM ELSE
  CASE WHEN d.ROW_NUM IS NOT NULL THEN d.ROW_NUM ELSE
    v.ROW_NUM
  END
END

现在,如果客户有3个司机和3辆车,我会得到3行而不是9行。这使得看起来每个驾驶员都与3辆车中的1辆相关联,但事实并非如此。同样,这是一个糟糕的设计,但有必要减少我给出的不合理限制返回的行数。

它似乎比webturner的答案更多的工作,但在我的实际情况中,我必须加入10个不同的表,超过500列,这样做的工作要少得多,而不是明确命名所有500列并填写所有每个表的剩余列的值为NULL。

尽管如此,这对大多数人来说可能没什么用处。在大多数情况下,如果你正在做这样的事情,你可能需要重新考虑你的设计,但在某些情况下你别无选择。