java - 为什么我的 native 查询比 JPA 查询更快?

标签 java sql postgresql jpa spring-data-jpa

我试图理解为什么我的 native 查询比 JPA 查询加载更多数据,但 native 查询速度要快得多。

这是我的 JPA 查询:

@NamedEntityGraph(
                name = "ShipmentTbl.report.scheduling-delivery",
                attributeNodes = {
                        @NamedAttributeNode("boxes"),
                        @NamedAttributeNode("pallets"),
                        @NamedAttributeNode("shipFrom"),
                        @NamedAttributeNode("shipmentReceive"),
                        @NamedAttributeNode("shipmentPurchaseOrders"),
                        @NamedAttributeNode("shipmentProducts"),
                        @NamedAttributeNode("distributionCenter"),
                        @NamedAttributeNode("scheduledDates")
                }
        )

.......

@EntityGraph(value = "ShipmentTbl.report.scheduling-delivery", type = EntityGraph.EntityGraphType.LOAD)
    @Query("SELECT s FROM ShipmentTbl s " +
            "JOIN s.shipmentReceive sr " +
            "WHERE (sr.receivedDatetime BETWEEN :startDate AND :endDate) " +
            "AND (:includeArchivedShipments = TRUE OR s.status <> 12)")
    List<ShipmentTbl> getDeliveryReportData(@Param(value = "startDate") Timestamp startDate,
                                            @Param(value = "endDate") Timestamp endDate,
                                            @Param(value = "includeArchivedShipments") boolean includeArchivedShipments);

这是我的 native 查询:

SELECT
s.id,
s.shipment_name,
 max(dc.name) as dc,
max(sf.country) as country,
s.status,
s.estimated_shipment_date,
string_agg(CAST(ssd.scheduled_shipment_date as TEXT), ',') as scheduled_shipment_date,
string_agg(CAST(ssd.scheduled_period_of_day as TEXT), ',') as scheduled_period_of_day,
s.courier,
s.courier_amount,
 s.courier_currency,
s.total_volume,
s.measurement_unit,
s.total_weight,
s.weight_unit,
s.tracking_number,
s.auth_code,
s.booked_in_by_user,
s.tenant,
(SELECT stm.date FROM status_transition_message stm WHERE (stm.initial_status = 0 AND stm.final_status = 1 OR stm.initial_status = 0 AND stm.final_status = 7) AND stm.shipment_id = s.id ORDER BY date ASC LIMIT 1) AS submitted_dated,
(SELECT stm.user_full_name FROM status_transition_message stm WHERE (stm.initial_status = 0 AND stm.final_status = 1 OR stm.initial_status = 0 AND stm.final_status = 7) AND stm.shipment_id = s.id ORDER BY date ASC LIMIT 1) AS submitted_by,
(SELECT stm.date FROM status_transition_message stm WHERE stm.initial_status = 1 AND stm.final_status = 3 AND stm.shipment_id = s.id ORDER BY date ASC LIMIT 1) AS first_time_in_progress,
(SELECT stm.date FROM status_transition_message stm WHERE stm.initial_status = 3 AND stm.final_status = 4 AND stm.shipment_id = s.id ORDER BY date ASC LIMIT 1) AS approved_date,
(SELECT stm.user_full_name FROM status_transition_message stm WHERE stm.initial_status = 3 AND stm.final_status = 4 AND stm.shipment_id = s.id ORDER BY date ASC LIMIT 1) AS tt_user,
(SELECT stm.user_full_name FROM status_transition_message stm WHERE stm.initial_status = 4 AND (stm.final_status = 10 OR stm.final_status = 11) AND stm.shipment_id = s.id ORDER BY date ASC LIMIT 1) AS received_by,
(SELECT string_agg(CAST(purchase_order_id AS TEXT), ',') FROM (SELECT spo.purchase_order_id FROM shipment_purchase_orders spo WHERE spo.shipment_id = s.id) AS purchase_orders) AS purchase_orders,
(SELECT string_agg(to_char(bookin_datetime, 'YYYY-MM-DD HH24:MI:SS.US'), ',') FROM (SELECT sr.bookin_datetime FROM shipment_receive sr WHERE sr.shipment_id = s.id) AS bd) AS bookin_datetimes,
(SELECT string_agg(to_char(received_datetime, 'YYYY-MM-DD HH24:MI:SS.US'), ',') FROM (SELECT sr.received_datetime FROM shipment_receive sr WHERE sr.shipment_id = s.id) AS dd) AS received_datetimes,
(SELECT string_agg(CAST(file_id AS TEXT), ',') FROM (SELECT spd.file_id FROM shipment_packing_documents spd WHERE spd.shipment_id = s.id) AS packing_docs) AS packing_doc_ids,
(SELECT string_agg(CAST(file_id AS TEXT), ',') FROM (SELECT std.file_id FROM shipment_tech_documents std WHERE std.shipment_id = s.id) AS tech_docs) AS tech_doc_ids,
(SELECT string_agg(CAST(number AS TEXT), ',') FROM (SELECT scd.number FROM shipment_customs_documents scd WHERE scd.shipment_id = s.id) AS customs_numbers) AS customs_numbers,
(SELECT string_agg(CAST(file_id AS TEXT), ',') FROM (SELECT file_id FROM shipment_invoices si WHERE si.shipment_id = s.id) AS invoice_docs) AS invoice_doc_ids,
(SELECT string_agg(CAST(number AS TEXT), ', ') FROM (SELECT si.number FROM shipment_invoices si WHERE si.shipment_id = s.id) AS invoice_numbers) AS invoice_numbers,
(SELECT sum(si.amount) FROM shipment_invoices si WHERE si.shipment_id = s.id) AS invoice_amount,
(SELECT sum(p.pallets_number) FROM pallets p WHERE p.shipment_id = s.id) AS pallets_number,
(SELECT sum(p.pallets_number * p.boxes_number) FROM pallets p WHERE p.shipment_id = s.id) AS boxes_by_pallet,
(SELECT sum(b.boxes_number) FROM boxes b WHERE b.shipment_id = s.id) AS boxes,
(SELECT string_agg(CAST(products AS TEXT), ',') FROM (SELECT sp.stock_order_id, sp.shipping_quantity FROM shipment_products sp WHERE sp.shipment_id = s.id) AS products) AS products,
(SELECT string_agg(DISTINCT CAST(status AS TEXT), ',') FROM products_status WHERE shipment_id = s.id) AS product_status,
s.tt_priority
(SELECT stm.date FROM status_transition_message stm WHERE (stm.initial_status = 0 AND stm.final_status = 1 OR stm.initial_status = 7 AND stm.final_status = 1) AND stm.shipment_id = s.id ORDER BY date ASC LIMIT 1) AS first_time_in_waiting_authorization,
"  s.submitted_date as last_submitted_date
"FROM shipment_receive sr 
JOIN shipment s ON sr.shipment_id = s.id
LEFT JOIN shipment_scheduled_date ssd on ssd.shipment_id = s.id
JOIN distribution_center dc ON s.ship_to = dc.id
JOIN address sf ON s.ship_from = sf.id
WHERE sr.received_datetime BETWEEN :startDate AND :endDate
AND (:includeArchivedShipments = TRUE OR s.status <> 12)
GROUP BY s.id";

我想指出,这两个查询并不等效,但它们确实加载了很多公共(public)字段,并且在 WHERE 子句中具有相同的条件。

我知道第二个查询看起来又长又无聊,但要点是,我的实体都是延迟加载的。没有一个急切加载的字段。因此,在 JPA 查询中,EntityGraph 中的字段是唯一急切加载的字段。尽管如此,我的第一个查询 (JPA) 中发生的联接比第二个查询中发生的联接要少,第二个查询联接与第一个查询相同的表以及更多表。然而,第二个查询平均需要大约 11 秒才能运行,而第一个查询则需要大约 20 秒,两者都具有完全相同的参数。

有人能解释一下为什么吗?我知道第一个最终会加载实体中更多的“正常”字段(如字符串、日期、整数...),但我仍然希望它运行得更快。

(如果足够相关,我还可以发布 Hibernate 使用 JPA 查询构建的 native SQL 查询)

编辑:这是 Hibernate 从初始 JPA 查询生成的 SQL 查询:

select
            shipmenttb0_.id as id1_13_0_,
            shipmentre1_.id as id1_22_1_,
            boxes2_.id as id1_1_2_,
            shipmentpu3_.id as id1_21_3_,
            addresstbl4_.id as id1_0_4_,
            scheduledd5_.id as id1_23_5_,
            pallets6_.id as id1_7_6_,
            shipmentpr7_.id as id1_20_7_,
            distributi8_.id as id1_2_8_,
            shipmenttb0_.archived_date as archived2_13_0_,
            shipmenttb0_.auth_code as auth_cod3_13_0_,
            shipmenttb0_.authorization_date as authoriz4_13_0_,
            shipmenttb0_.booked_in_by_user as booked_i5_13_0_,
            shipmenttb0_.business_channel as business6_13_0_,
            shipmenttb0_.courier as courier7_13_0_,
            shipmenttb0_.courier_amount as courier_8_13_0_,
            shipmenttb0_.courier_currency as courier_9_13_0_,
            shipmenttb0_.ship_to as ship_to40_13_0_,
            shipmenttb0_.estimated_shipment_date as estimat10_13_0_,
            shipmenttb0_.forecast_scheduled_date as forecas11_13_0_,
            shipmenttb0_.last_updated_date as last_up12_13_0_,
            shipmenttb0_.measurement_unit as measure13_13_0_,
            shipmenttb0_.original_submitted_date as origina14_13_0_,
            shipmenttb0_.packaging_type as packagi15_13_0_,
            shipmenttb0_.placeholder_message as placeho16_13_0_,
            shipmenttb0_.scheduled_period_of_day as schedul17_13_0_,
            shipmenttb0_.scheduled_shipment_date as schedul18_13_0_,
            shipmenttb0_.ship_from as ship_fr41_13_0_,
            shipmenttb0_.ship_origin as ship_or42_13_0_,
            shipmenttb0_.shipment_name as shipmen19_13_0_,
            shipmenttb0_.status as status20_13_0_,
            shipmenttb0_.submitted_date as submitt21_13_0_,
            shipmenttb0_.supplier_contact_email as supplie22_13_0_,
            shipmenttb0_.supplier_contact_name as supplie23_13_0_,
            shipmenttb0_.supplier_contact_phone_number as supplie24_13_0_,
            shipmenttb0_.supplier_email as supplie25_13_0_,
            shipmenttb0_.supplier_secondary_contact_email as supplie26_13_0_,
            shipmenttb0_.supplier_secondary_contact_name as supplie27_13_0_,
            shipmenttb0_.supplier_secondary_contact_phone_number as supplie28_13_0_,
            shipmenttb0_.tenant as tenant29_13_0_,
            shipmenttb0_.total_received_boxes as total_r30_13_0_,
            shipmenttb0_.total_units as total_u31_13_0_,
            shipmenttb0_.total_value as total_v32_13_0_,
            shipmenttb0_.total_volume as total_v33_13_0_,
            shipmenttb0_.total_weight as total_w34_13_0_,
            shipmenttb0_.tracking_number as trackin35_13_0_,
            shipmenttb0_.tt_note as tt_note36_13_0_,
            shipmenttb0_.tt_priority as tt_prio37_13_0_,
            shipmenttb0_.updated_by_user as updated38_13_0_,
            shipmenttb0_.weight_unit as weight_39_13_0_,
            shipmentre1_.bookin_datetime as bookin_d2_22_1_,
            shipmentre1_.received_type as received3_22_1_,
            shipmentre1_.delay_days as delay_da4_22_1_,
            shipmentre1_.received_boxes as received5_22_1_,
            shipmentre1_.received_datetime as received6_22_1_,
            shipmentre1_.shipment_scheduled_date as shipment9_22_1_,
            shipmentre1_.session_active as session_7_22_1_,
            shipmentre1_.shipment_id as shipmen10_22_1_,
            shipmentre1_.status as status8_22_1_,
            shipmentre1_.shipment_id as shipmen10_22_0__,
            shipmentre1_.id as id1_22_0__,
            boxes2_.added_time as added_ti2_1_2_,
            boxes2_.boxes_number as boxes_nu3_1_2_,
            boxes2_.height as height4_1_2_,
            boxes2_.length as length5_1_2_,
            boxes2_.shipment_id as shipment7_1_2_,
            boxes2_.width as width6_1_2_,
            boxes2_.shipment_id as shipment7_1_1__,
            boxes2_.id as id1_1_1__,
            shipmentpu3_.purchase_order_id as purchase2_21_3_,
            shipmentpu3_.shipment_id as shipment3_21_3_,
            shipmentpu3_.shipment_id as shipment3_21_2__,
            shipmentpu3_.id as id1_21_2__,
            addresstbl4_.address_line1 as address_2_0_4_,
            addresstbl4_.address_line2 as address_3_0_4_,
            addresstbl4_.address_line3 as address_4_0_4_,
            addresstbl4_.address_line4 as address_5_0_4_,
            addresstbl4_.city as city6_0_4_,
            addresstbl4_.company_name as company_7_0_4_,
            addresstbl4_.contact_email as contact_8_0_4_,
            addresstbl4_.contact_name as contact_9_0_4_,
            addresstbl4_.contact_phone as contact10_0_4_,
            addresstbl4_.country as country11_0_4_,
            addresstbl4_.postal_code as postal_12_0_4_,
            addresstbl4_.time_zone_id as time_zo13_0_4_,
            addresstbl4_.title as title14_0_4_,
            scheduledd5_.estimated_product_item_units as estimate2_23_5_,
            scheduledd5_.expected_boxes as expected3_23_5_,
            scheduledd5_.expected_volume as expected4_23_5_,
            scheduledd5_.scheduled_period_of_day as schedule5_23_5_,
            scheduledd5_.scheduled_shipment_date as schedule6_23_5_,
            scheduledd5_.shipment_id as shipment7_23_5_,
            scheduledd5_.shipment_id as shipment7_23_3__,
            scheduledd5_.id as id1_23_3__,
            pallets6_.added_time as added_ti2_7_6_,
            pallets6_.boxes_number as boxes_nu3_7_6_,
            pallets6_.height as height4_7_6_,
            pallets6_.length as length5_7_6_,
            pallets6_.pallets_number as pallets_6_7_6_,
            pallets6_.shipment_id as shipment8_7_6_,
            pallets6_.width as width7_7_6_,
            pallets6_.shipment_id as shipment8_7_4__,
            pallets6_.id as id1_7_4__,
            shipmentpr7_.country_of_origin as country_2_20_7_,
            shipmentpr7_.hs_code as hs_code3_20_7_,
            shipmentpr7_.is_new as is_new4_20_7_,
            shipmentpr7_.manufacturer_id as manufact5_20_7_,
            shipmentpr7_.note as note6_20_7_,
            shipmentpr7_.preferential_origin as preferen7_20_7_,
            shipmentpr7_.shipment_id as shipmen10_20_7_,
            shipmentpr7_.shipping_quantity as shipping8_20_7_,
            shipmentpr7_.stock_order_id as stock_or9_20_7_,
            (select
                1) as formula0_7_,
            shipmentpr7_.shipment_id as shipmen10_20_5__,
            shipmentpr7_.id as id1_20_5__,
            distributi8_.address_id as address_9_2_8_,
            distributi8_.label as label2_2_8_,
            distributi8_.manufacturer_id_required as manufact3_2_8_,
            distributi8_.max_product_items_units as max_prod4_2_8_,
            distributi8_.max_products_units as max_prod5_2_8_,
            distributi8_.max_volume as max_volu6_2_8_,
            distributi8_.name as name7_2_8_,
            distributi8_.volume_unit as volume_u8_2_8_ 
        from
            shipment shipmenttb0_ 
        inner join
            shipment_receive shipmentre1_ 
                on shipmenttb0_.id=shipmentre1_.shipment_id 
        left outer join
            boxes boxes2_ 
                on shipmenttb0_.id=boxes2_.shipment_id 
        left outer join
            shipment_purchase_orders shipmentpu3_ 
                on shipmenttb0_.id=shipmentpu3_.shipment_id 
        left outer join
            address addresstbl4_ 
                on shipmenttb0_.ship_from=addresstbl4_.id 
        left outer join
            shipment_scheduled_date scheduledd5_ 
                on shipmenttb0_.id=scheduledd5_.shipment_id 
        left outer join
            pallets pallets6_ 
                on shipmenttb0_.id=pallets6_.shipment_id 
        left outer join
            shipment_products shipmentpr7_ 
                on shipmenttb0_.id=shipmentpr7_.shipment_id 
        left outer join
            distribution_center distributi8_ 
                on shipmenttb0_.ship_to=distributi8_.id 
        where
            (
                shipmentre1_.received_datetime between ? and ?
            ) 
            and (
                ?=true 
                or shipmenttb0_.status<>12
            )

就像我说的,它执行了一些连接,但只不过是其他 native 查询,所以它应该更快。

最佳答案

您的 JPA 查询很可能没有像您期望的那样运行单个查询。我知道您说过您认为您已加载所有 LAZY 内容,但您确实需要打开 SQL 日志记录才能解决这一问题。

无论您的日志记录平台是什么,请将此记录器更改为DEBUG

org.hibernate.SQL:调试

关于java - 为什么我的 native 查询比 JPA 查询更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58630768/

相关文章:

json - 获取自下而上的嵌套json以在postgresql中进行查询

java - EclipseLink 复合 PK 与 FK

java - 比 Stream.peek() 更好的方法

mysql - 多个并且在 INNER JOIN 上?

sql - Group By 子句中的列名无效错误

c# - 带有 OUTPUT 子句的 SQL Server BULK INSERT

java - String类中的split方法和Apache StringUtils中的split方法有什么区别?

java - Java 中的基本递归算法

c# - EF6 postgresql 数据库优先,无法生成模型

postgresql - 致命 : password authentication failed for user "postgres" (postgresql 11 with pgAdmin 4)