将三个使用 1:* 关系的查询合并为一个查询
Posted
技术标签:
【中文标题】将三个使用 1:* 关系的查询合并为一个查询【英文标题】:Merging three queries that use 1:* relationships into one query 【发布时间】:2012-11-30 20:11:08 【问题描述】:假设我有四个要读取的表:
客户
customer_id, customer_name
1 Joe Bolggs
客户订单
customer_id, order_no
----------------------------
1 1
1 2
1 3
客户地址
customer_id address
----------------------------
1 11 waterfall road
1 23 The broadway
customer_tel_no
customer_id number
----------------------------
1 523423423432
1 234342342343
上面显示的客户信息(对于 id=1 的客户)将存储在如下所示的 Java 对象中
public class Customer
String customer_id;
String customerName;
ArrayList<String> customerOrders;
ArrayList<String> customerAddress;
ArrayList<String> customerTelephoneNumbers;
我能想到的获得上述信息的唯一方法是使用三个查询。原因是客户表和其他每个表之间存在 1:* 关系。为了获取数据,我正在做这样的事情:
Customer customer = new Customer()
String customerSQL = "Select * from customer where customer_id = ?";
statement = getConnection().prepareStatement(contactsQuery);
statement.setString(1,1);
resultSet = statement.executeQuery();
while (resultSet.next())
customer.customer_id = resultSet.get(1); //No getters/setters in this example
customer.customerName = resultSet.get(2);
String customerOrdersSQL = "Select * from customer_orders where customer_id = ?";
statement = getConnection().prepareStatement(customerOrdersSQL);
statement.setString(1,1);
resultSet = statement.executeQuery();
customer.customerOrders = new ArrayList();
while (resultSet.next())
customer.customerOrders.add(resultSet.getString(2); // all the order numbers
String customerAddressesSQL = "Select * from customer_addresses where customer_id = ?";
statement = getConnection().prepareStatement(customerAddressesSQL );
statement.setString(1,1);
resultSet = statement.executeQuery();
customer.customerAddresses = new ArrayList();
while (resultSet.next())
customer.customerAddresses.add(resultSet.getString(2); // all the addresses
String customerTelSQL = "Select * from customer_tel_no where customer_id = ?";
statement = getConnection().prepareStatement(customerTelSQL);
statement.setString(1,1);
resultSet = statement.executeQuery();
customer.customerTelephoneNumbers = new ArrayList();
while (resultSet.next())
customer.customerTelephoneNumbers.add(resultSet.getString(2); // all the order numbers
上面的问题是我对数据库进行了三个调用。请问有没有办法可以将以上内容合并到一个查询中?
我看不出连接是如何工作的,因为例如,customer 和 customer_orders 之间的连接将为 customer_orders 中的每一行返回一个客户行。无论如何我可以将上述三个查询合并为一个吗?
【问题讨论】:
Ziggy:您写道“问题......是我正在对数据库进行三个调用”。为什么这是个问题?如果您担心代码的冗长,请使用 ORM 框架(如 @enno 建议的那样)。如果您担心长期维护,也可以使用 ORM 框架。如果您担心潜在的性能问题,请等到看到实际的性能问题,然后重构(预测性能问题可能会导致不必要的复杂性)。 【参考方案1】:我认为这样的事情会起作用:
SELECT c.customer_id, c.customer_name, o.order_no, a.address, t.number
FROM customer c LEFT JOIN customer_orders o ON c.customer_id = o.customer_id
LEFT JOIN customer_addresses a ON c.customer_id = a.customer_id
LEFT JOIN customer_tel_no t ON c.customer_id = t.customer_id
WHERE c.customer_id = ?
然后,在你的代码中,在你执行查询之后:
while (resultSet.next())
customer.customerOrders.add(resultSet.getString(3));
customer.customerAddresses.add(resultSet.getString(4));
customer.customerTelephoneNumbers.add(resultSet.getString(5));
当然,这并没有考虑到在此过程中您将拥有空值这一事实,因此我建议您检查空值以确保您不会在数组列表中添加大量垃圾。不过,这可能比 3 个单独的查询成本低得多。
【讨论】:
我确实尝试过,但问题是父表中的每一行对于子表中的每一行都是重复的。这是无法避免的吗? 那么只需从您的查询中省略这些列。需要明确的是,您仍然需要至少 2 个查询:一个用于填充您的客户“标题”信息,然后一个用于填充相关数据。 我的意思是,例如,customer_name 仅在结果的第一次迭代中是必需的,但它将存在于所有迭代中(如果有意义的话),因此不能省略该列。 我的意思是您需要使用 2 个查询:您的第一个查询将是客户表,并将获得 customer_id 和 customer_name。您使用该查询来填充对象上的相关字段。然后你可以执行我建议的查询来填写相关数据(订单、地址、电话号码)。您不需要在第二个查询中包含两个客户字段。 嗯,好的。这是有道理的。【参考方案2】:没有什么可以阻止您将连接的结果迭代和处理到您的客户对象中。如果您的应用程序足够复杂,您可以研究 ORM 框架,它会在幕后为您做到这一点。如果您正在使用 JavaEE,请查看 JPA。
【讨论】:
@ziggy 是的,为什么不呢。您可以通过添加 ORDER BY customer_id 来做到这一点 对不起,我不小心删除了我询问是否应该在迭代结果时存储 id 的问题。 :( @ziggy 如果您希望从表中提取所有客户(例如),那么是的,在执行 1)创建具有 id 的客户对象时迭代连接和排序的结果集,2)如果 id 匹配,则继续向该客户对象添加电话/地址等,3)如果 id 更改,将客户对象存储到您的列表中,为新 id 创建一个新客户对象 -> 继续这样做,直到您用尽结果集【参考方案3】:使用此查询并减少调用次数。并且,在数据的while循环过程中。
选择 customer.customer_id,customer.customer_name,order_no,address,number
来自客户、客户订单、客户地址、客户电话号码
其中 customer.customer_id = customer_orders.customer_id 和 customer.customer_id = customer_addresses.customer_id 和 customer.customer_id = customer_tel_no.customer_id
【讨论】:
您可以选择您的代码(在编辑答案时)并点击
按钮。它会将您的代码向右缩进 4 列,因此它将作为代码可见。以上是关于将三个使用 1:* 关系的查询合并为一个查询的主要内容,如果未能解决你的问题,请参考以下文章
如何将三个选择查询合并为一个,以在 Oracle 中实现如下所需的结果?
使用 Laravel 将两个模型合并到一个分页查询中,并带有急切加载的关系