I am currently trying to analyze why one particular query performs worse than expected. While doing that i noticed that the query plan shown when executing the query manually does not match up with the query plan used when the query is dispatched by our server. This makes it very hard to analyze the performance issue, since i then only have the cached plan available and the manually executed query performs far better.
This is the query being executed (simplified):
SELECT h.* FROM horse h
INNER JOIN horse_number hn ON hn.horse_id = h.id
INNER JOIN number n ON n.id = hn.number_id
WHERE lower(n.number) LIKE '%de 224%' escape '!' LIMIT 30;
And some table metrics:
Sizes (Rows):
Here are the two plans (zoomed out):
1. Cached Plan (executed by our server):

2. Plan for manual execution of the same query:

And here are some performance metrics to highlight the difference in execution time
1. Executed by our server

and a trace log where the execution can also be observed:
# begin setAutoCommit (thread 130313, con-id 344489) at 2022-04-12 13:16:51.247386
# con info [con-id 344489, tx-id 26, cl-pid 24592, cl-ip 10.78.26.254, user: xxx, schema: xxx]
con_c44489.setautocommit(False)
# begin prepareStatement (thread 51835, con-id 344489) at 2022-04-12 13:16:51.247774
# con info [con-id 344489, tx-id 26, cl-pid 24592, cl-ip 10.78.26.254, user: xxx, schema: xxx]
cursor_140289778665472_c44489 = con_c44489.cursor()
# end prepareStatement (thread 51835, con-id 344489) at 2022-04-12 13:16:51.248029
# begin PreparedStatement_execute (thread 130313, con-id 344489) at 2022-04-12 13:16:51.249261
# con info [con-id 344489, tx-id 26, cl-pid 24592, cl-ip 10.78.26.254, user: xxx, schema: xxx]
cursor_140289778665472_c44489.execute(''' select horse0_.id as id1_135_, horse0_.created_by_account_id as created_by_account_id26_135_, horse0_.created_by_organization_id as created_by_organization_id27_135_, horse0_.creation_time as creation_time2_135_, horse0_.last_modified as last_modified3_135_, horse0_.modified_by_account_id as modified_by_account_id28_135_, horse0_.breed as breed4_135_, horse0_.breeding_name as breeding_name5_135_, horse0_.color as color6_135_, horse0_.dam_id as dam_id29_135_, horse0_.dam_name as dam_name7_135_, horse0_.dam_of_dam as dam_of_dam8_135_, horse0_.dam_of_sire as dam_of_sire9_135_, horse0_.date_of_birth as date_of_birth10_135_, horse0_.fei_id as fei_id11_135_, horse0_.first_breeding_activity_year as first_breeding_activity_year12_135_, horse0_.gender as gender13_135_, horse0_.height as height14_135_, horse0_.image_url as image_url15_135_, horse0_.is_pony as is_pony16_135_, horse0_.last_coggins_date as last_coggins_date17_135_, horse0_.last_vaccination_date as last_vaccination_date18_135_, horse0_.micro_chip_number as micro_chip_number19_135_, horse0_.name as name20_135_, horse0_.permanent_bridle_number as permanent_bridle_number21_135_, horse0_.sire_id as sire_id30_135_, horse0_.sire_name as sire_name22_135_, horse0_.sire_of_dam_id as sire_of_dam_id31_135_, horse0_.sire_of_dam_name as sire_of_dam_name23_135_, horse0_.sire_of_sire as sire_of_sire24_135_, horse0_.studbook_id as studbook_id32_135_, horse0_.ueln as ueln25_135_ from horse horse0_ inner join horse_national_registration nationalre1_ on horse0_.id=nationalre1_.horse_id inner join national_registration nationalre2_ on nationalre1_.national_registration_id=nationalre2_.id where lower(nationalre2_.number) like ? escape '!' limit ? ''', ('''%de 441410768301%''', 30))
# end PreparedStatement_execute (thread 130313, con-id 344489) at 2022-04-12 13:16:51.737367
# begin commit (thread 130312, con-id 344489) at 2022-04-12 13:16:54.044500
# con info [con-id 344489, tx-id 26, cl-pid 24592, cl-ip 10.78.26.254, user: xxx, schema: xxx]
con_c44489.commit()
# end commit (thread 130312, con-id 344489) at 2022-04-12 13:16:54.044659
2. Executed manually:

As you can see the manual execution is a lot faster and judging by the complexity of the query the execution time when executed by our server seems far too high. What are the next steps i should be talking to analyze this further?
EDIT: Zooned in query plans:
1. Server

2. Manual:

Thanks!
Request clarification before answering.
After the help of the comment section and more analyzing, the difference in query plans was caused by not using a prepared statement when executing the query manually, which obviously will have another query plan. After using the same prepared statement i was able to reproduce the bad performance and ultimately came to the conclusion that it was caused by a dynamic case insensitive search using the LOWER function.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 10 | |
| 5 | |
| 5 | |
| 5 | |
| 3 | |
| 2 | |
| 2 | |
| 1 | |
| 1 | |
| 1 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.