Querydsl: how to make left join by column - sql

Im trying to match this SQL query in querydsl
SELECT tr.* FROM test.TRIP_REQ tr left outer join test.ADDR_BOOK ab on tr.REQ_USERID=ab.USER_ID
I know how to make left join query if you join into identity column but struggle to make it work with joining on 2 alternative columns. tr.REQ_USERID and ab.USER_ID are not identity columns
This is my querydsl:
QTripReq qTripReq = QTripReq.tripReq;
QAddressBook qABook = QAddressBook.addressBook;
JPAQuery query = new JPAQuery(entityManager);
query.from(qTripReq).leftJoin(qABook).on(qTripReq.requestorUser.id.eq(qABook.user.id)).list(qTripReq);
This throws error:
Path expected for join! [select tripReq from com.TripReq tripReq left join ADDR_BOOK addressBook with tripReq.requestorUser.id = addressBook.user.id where tripReq.assignedCompany.id = ?1]

You need to add a target entity path to leftJoin(), so that
QTripReq qTripReq = QTripReq.tripReq;
QAddressBook qABook = QAddressBook.addressBook;
JPAQuery query = new JPAQuery(entityManager);
query.from(qTripReq).leftJoin(qTripReq.addressBook, qABook).on(qTripReq.requestorUser.id.eq(qABook.user.id)).list(qTripReq);
Take a look on Using joins section in docs.

Related

Spring Data JPA - Left Join with Multiple Criteria

I need to do a join using a JOIN TABLE ON ... AND ... using Spring Data JPA criteria builder.
I know I can do a basic join like so:
Join<ReportEntity, ProductEntity> productJoin = root.join("products", JoinType.LEFT);
But can I specify extra criteria for the join? If not, is there another way to achieve this using the Criteria Builder? This is the SQL query I'd like to reproduce:
SELECT r.id, p.rare
FROM REPORT r
LEFT JOIN PRODUCT p
ON r.id = p.report_id AND p.rare = 1
WHERE p.report_id IS NULL;
Note that specifying p.rare = 1 in the above query in the WHERE clause does not give the desired result, it needs to go in the ON clause.
Join<ReportEntity, ProductEntity> productJoin = root.join("products", JoinType.LEFT);
Predicate joinPredicate = criteriaBuilder.equal(root.get("id"), productJoin.get("reportId"));
Predicate rarePredicate = criteriaBuilder.equal(productJoin.get("rare"), 1);
productJoin.on(joinPredicate, rarePredicate);

JOIN EXPRESSION NOT SUPPORTED with 3 tables

I'm working with MS Access, trying to join 3 tables together. But i'm getting message "JOIN EXPRESSION NOT SUPPORTED.".
Basically I want to join just 2 tables which are A_01HWeekEHCalendar and A00_Plant. A00_Plant requires 3 column to join; Plant_Product, Plant_Code, and Plant_Name. Plant_Product, Plant_Code is ok, but there is no column to match Plant_Product. so I have to add A_ProductGroup
I'm not sure if it can't be done in one Query or it's error from my SYNTAX. or there will be another way to do that without separate Queries.
I also add a picture of what I want and try to run and get error.
Thanks.
SELECT
A_01HWeekEHCalendar.RunNo_H,
A_01HWeekEHCalendar.Audit_Week,
A_01HWeekEHCalendar.CurrentYear,
A_01HWeekEHCalendar.CurrentWeek,
A_01HWeekEHCalendar.TTSMonth,
A_01HWeekEHCalendar.Audit_Plant,
A_01HWeekEHCalendar.Audit_plantname,
A_01HWeekEHCalendar.Audit_Qacode,
A_01HWeekEHCalendar.Audit_Qaname,
A_01HWeekEHCalendar.Audit_Product,
A00_Plant.HCA_StartDate
FROM
(A_01HWeekEHCalendar LEFT JOIN A_ProductGroup
ON A_01HWeekEHCalendar.Audit_Product = A_ProductGroup.R_Code)
LEFT JOIN A00_Plant
ON A_01HWeekEHCalendar.Audit_plantname = A00_Plant.Plant_Name
AND A_01HWeekEHCalendar.Audit_Plant = A00_Plant.Plant_Code
AND A_ProductGroup.R_Code = A00_Plant.Plant_Product;
You are lucky because you want only one column. You can work around this problem using a correlated subquery:
SELECT . . .,
(SELECT TOP (1) A00_Plant.HCA_StartDate
FROM A00_Plant
WHERE A_01HWeekEHCalendar.Audit_plantname = A00_Plant.Plant_Name
AND
A_01HWeekEHCalendar.Audit_Plant = A00_Plant.Plant_Code AND
A_ProductGroup.R_Code = A00_Plant.Plant_Product
) as HCA_StartDate
FROM A_01HWeekEHCalendar LEFT JOIN
A_ProductGroup
ON A_01HWeekEHCalendar.Audit_Product = A_ProductGroup.R_Code ;

MS Access INNER JOIN/LEFT JOIN problems

I have the following SQL string which tries to combine an INNER JOIN with a LEFT JOIN in the FROM section.
As you can see I use table VIP_APP_VIP_SCENARIO_DETAIL_LE to perform the query. When I use it against this table, Access give me an "Invalid Operation" error.
Interestingly, when I use the EXACT same query using the VIP_APP_VIP_SCENARIO_DETAIL_BUDGET or VIP_APP_VIP_SCENARIO_DETAIL_ACTUALS table, it performs flawlessly.
So why would it work on two tables but not the other? All fields are in all tables and the data types are correct.
As a side note: on the query with the error, if I change the LEFT JOIN to an INNER JOIN, it runs with no problem! I really need a LEFT JOIN though.
SELECT
D.MATERIAL_NUMBER,
D.MATERIAL_DESCRIPTION,
D.PRODUCTION_LOT_SIZE,
D.STANDARDS_NAME,
D.WORK_CENTER,
S.OP_SHORT_TEXT,
S.OPERATION_CODE,
D.LINE_SPEED_UPM,
D.PERCENT_STD,
D.EQUIPMENT_SU,
D.EQUIPMENT_CU,
D.OPERATOR_NUM,
V.COSTING_LOT_SIZE,
V.VOL_TOTAL_ADJ
FROM
([STDS_SCENARIO: TEST] AS D INNER JOIN MASTER_SUMMARY AS S ON
D.MATERIAL_NUMBER = S.MATERIAL_NUMBER AND D.WORK_CENTER = S.WORK_CENTER)
LEFT JOIN
(SELECT ITEM_CODE, COSTING_LOT_SIZE, VOL_TOTAL_ADJ
FROM
VIP_APP_VIP_SCENARIO_DETAIL_LE
WHERE SCENARIO_ID = 16968) AS V ON D.MATERIAL_NUMBER = V.ITEM_CODE
ORDER BY D.MATERIAL_NUMBER, D.STANDARDS_NAME, S.OPERATION_CODE;
tried to mock this up in SQL server with some tables of my own, but the structure seemed to work, this follows the pattern referenced above. (hopefully no syntax errors left here)
SELECT * FROM (
select
D.MATERIAL_NUMBER,
D.MATERIAL_DESCRIPTION,
D.PRODUCTION_LOT_SIZE,
D.STANDARDS_NAME,
D.WORK_CENTER,
S.OP_SHORT_TEXT,
S.OPERATION_CODE,
D.LINE_SPEED_UPM,
D.PERCENT_STD,
D.EQUIPMENT_SU,
D.EQUIPMENT_CU,
D.OPERATOR_NUM
FROM [STDS_SCENARIO: TEST] D
INNER JOIN MASTER_SUMMARY S
ON D.MATERIAL_NUMBER = S.MATERIAL_NUMBER AND D.WORK_CENTER = S.WORK_CENTER) AS J
LEFT JOIN
(SELECT ITEM_CODE, COSTING_LOT_SIZE, VOL_TOTAL_ADJ
FROM
VIP_APP_VIP_SCENARIO_DETAIL_LE
WHERE SCENARIO_ID = 16968) AS V ON J.MATERIAL_NUMBER = V.ITEM_CODE
ORDER BY J.MATERIAL_NUMBER, J.STANDARDS_NAME, J.OPERATION_CODE;
Had help from a friend and we discovered that it was a casting problem between a linked Oracle table and the Access table. To fix the problem we casted both sides of the linked fields to a string:
CSTR(D.[MATERIAL_NUMBER]) = CSTR(V.[ITEM_CODE])

Why does the Inner Join query not work while multi where clause does

Lets say I run a crooked car company. Let's say I have the following table:
car_engine_mileage_counters which is a join table from car_engines onto mileage_counters also storing a calculated field of mileage
Lets also say that I encode a coefficient at the engine block level in my factory on an engine template.
UPDATE car_engine_mileage_counters
SET mileage = mileage_counters.mileage * coefficients.coefficient
FROM car_engines
INNER JOIN engine_templates
ON car_engines.template_id = engine_templates.id
INNER JOIN mileage_counters
ON mileage_counters.id = car_engine_mileage_counters.mileage_counter_id
INNER JOIN mileage_counter_templates
ON mileage_counter.template_id = mileage_counter_templates.id
INNER JOIN coefficients
ON coefficients.mileage_counter_template_id = mileage_counter_templates.id
WHERE coefficients.engine_template_id = engine_template.id AND car_engines.id = car_engine_mileage_counters.engine_id;
This (clearly fictitious) example fails with:
ERROR: invalid reference to FROM-clause entry for table
"car_engine_mileage_counters"
LINE 7: ON mileage_counters.id = car_engine_mileage_counters...
^
HINT: There is an entry for table "measure_instances_question_instances", but it cannot be referenced
from this part of the query.
Enumerating all tables in a single FROM clause, and using WHERE AND in place of all INNER JOINs however works fine.
My question is, why? What is wrong with the inner join query? How can I fix it? Does it matter?
UPDATE cem
SET mileage = mileage_counters.mileage * coefficients.coefficient
FROM car_engine_mileage_counters cem
INNER JOIN car_engines
ON car_engines.id = cem.engine_id
INNER JOIN engine_templates
ON car_engines.template_id = engine_templates.id
INNER JOIN mileage_counters
ON mileage_counters.id = cem.mileage_counter_id
INNER JOIN mileage_counter_templates
ON mileage_counter.template_id = mileage_counter_templates.id
INNER JOIN coefficients
ON coefficients.mileage_counter_template_id = mileage_counter_templates.id
WHERE coefficients.engine_template_id = engine_template.id AND car_engines.id = cem.engine_id;

Ignore null values in select statement

I'm trying to retrieve a list of components via my computer_system, BUT if a computer system's graphics card is set to null (I.e. It has an onboard), the row isn't returned by my select statement.
I've been trying to use COALESCE without results. I've also tried with and OR in my WHERE clause, which then just returns my computer system with all different kinds of graphic cards.
Relevant code:
SELECT
computer_system.cs_id,
computer_system.cs_name,
motherboard.name,
motherboard.price,
cpu.name,
cpu.price,
gfx.name,
gfx.price
FROM
public.computer_case ,
public.computer_system,
public.cpu,
public.gfx,
public.motherboard,
public.ram
WHERE
computer_system.cs_ram = ram.ram_id AND
computer_system.cs_cpu = cpu.cpu_id AND
computer_system.cs_mb = motherboard.mb_id AND
computer_system.cs_case = computer_case.case_id AND
computer_system.cs_gfx = gfx.gfx_id; <-- ( OR computer_system.cs_gfx IS NULL)
Returns:
1;"Computer1";"Fractal Design"; 721.00; "MSI Z87"; 982.00; "Core i7 I7-4770K "; 2147.00; "Crucial Gamer"; 1253.00; "ASUS GTX780";3328.00
Should I use Joins? Is there no easy way to say return the requested row, even if there's a bloody NULL value. Been struggling with this for at least 2 hours.
Tables will be posted if needed.
EDIT: It should return a second row:
2;"Computer2";"Fractal Design"; 721.00; "MSI Z87"; 982.00; "Core i7 I7-4770K "; 2147.00; "Crucial Gamer"; 1253.00; "null/nothing";null/nothing
You want a LEFT OUTER JOIN.
First, clean up your code so you use ANSI joins so it's readable:
SELECT
computer_system.cs_id,
computer_system.cs_name,
motherboard.name,
motherboard.price,
cpu.name,
cpu.price,
gfx.name,
gfx.price
FROM
public.computer_system
INNER JOIN public.computer_case ON computer_system.cs_case = computer_case.case_id
INNER JOIN public.cpu ON computer_system.cs_cpu = cpu.cpu_id
INNER JOIN public.gfx ON computer_system.cs_gfx = gfx.gfx_id
INNER JOIN public.motherboard ON computer_system.cs_mb = motherboard.mb_id
INNER JOIN public.ram ON computer_system.cs_ram = ram.ram_id;
Then change the INNER JOIN on public.gfx to a LEFT OUTER JOIN:
LEFT OUTER JOIN public.gfx ON computer_system.cs_gfx = gfx.gfx_id
See PostgreSQL tutorial - joins.
I very strongly recommend reading an introductory tutorial to SQL - at least the PostgreSQL tutorial, preferably some more material as well.
It looks like it's just a bracket placement issue. Pull the null check and the graphics card id comparison into a clause by itself.
...
computer_system.cs_case = computer_case.case_id AND
(computer_system.cs_gfx IS NULL OR computer_system.cs_gfx = gfx.gfx_id)
Additionally, you ask if you should use joins. You are in fact using joins, by virtue of having multiple tables in your FROM clause and specifying the join criteria in the WHERE clause. Changing this to use the JOIN ON syntax might be a little easier to read:
FROM sometable A
JOIN someothertable B
ON A.somefield = B.somefield
JOIN somethirdtable C
ON A.somefield = C.somefield
etc
Edit:
You also likely want to make the join where you expect the null value to be a left outer join:
SELECT * FROM
first_table a
LEFT OUTER JOIN second_table b
ON a.someValue = b.someValue
If there is no match in the join, the row from the left side will still be returned.