将三个使用 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 将两个模型合并到一个分页查询中,并带有急切加载的关系

Laravel:将 2 个查询合并为 1 个

Microsoft Access 查询 - 将两个查询合并为一个

将嵌套查询转换为 sql 视图

sql查询结果部分替换