In the following relationship, I would like to find instances of entity A not referenced by B.
#Entity
public class A {
}
#Entity
public class B {
#OneToOne(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumn(name = "a_id")
private A a;
}
How can I write a JPQL query for this?
You can do the left join with A and check if b.id is null to find all
rows of A which doesn't have reference in B.
JPQL:
SELECT a FROM A a LEFT JOIN B b on b.a = a where b.id IS NULL
Mysql:
select a.id from A a left join B b on b.a_id = a.id where b.id is NULL;
Related
TableAlias isn't working with multiple joins.
The query:
var q = Db.From<Blog>(Db.TableAlias("b"))
.LeftJoin<Blog, BlogToBlogCategory>((b,btb)=> b.Id == btb.BlogId, Db.TableAlias("btbc"))
.Join<BlogToBlogCategory, BlogCategory>((bt,bc)=>bt.BlogCategoryId == bc.Id, Db.TableAlias("cats"))
.GroupBy(x => x.Id);
.Select("b.*, json_agg(cats) as BlogCategoriesJson");
var results = Db.Select<BlogQueryResponse>(q);
Generates this SQL:
SELECT b.*, json_agg(cats) as BlogCategoriesJson
FROM "blog" "b" LEFT JOIN "blog_to_blog_category" "btbc" ON ("b"."id" = "btbc"."blog_id") INNER JOIN "blog_category" "cats" ON ("blog_to_blog_category"."blog_category_id" = "cats"."id")
GROUP BY "b"."id"
This causes error because it is referencing "blog_to_blog_category" instead of btbc
The Db.TableAlias() only provides an alias for the target join table, your inner join does not specify the alias to use for the source table so it references the full table name as expected.
You can use Sql.TableAlias() in your LINQ Expression to reference a table alias, e.g:
var q = Db.From<Blog>(Db.TableAlias("b"))
.LeftJoin<Blog, BlogToBlogCategory>((b,btb)=> b.Id == btb.BlogId, Db.TableAlias("btbc"))
.Join<BlogToBlogCategory, BlogCategory>((bt,bc)=>
Sql.TableAlias(bt.BlogCategoryId, "btbc") == bc.Id, Db.TableAlias("cats"))
.GroupBy(x => x.Id);
.Select("b.*, json_agg(cats) as BlogCategoriesJson");
I'm trying to get paginates orders, sorted on the cration date of the current version (currentVersion = true)
Here is the Order class :
#Entity
public class Offer extends Model {
#Id
public Long id;
....
#OneToMany(mappedBy = "offer", fetch = FetchType.EAGER)
public List<Version> versions;
...
}
Here is the version class
#Entity
public class Version extends Model {
#Id
public Long id;
#ManyToOne
#JoinColumn(name = "OFFER_ID")
public Offer offer;
public Boolean currentVersion = false;
public Date creation;
...
}
Here is the fetch code as I found on eBean documentation :
Query<Offre> query = Ebean.find(Offer.class);
List<Offer> offers = query.fetch("versions")
.where()
.eq("versions.currentVersion", true)
.orderBy("versions.creation desc nulls last")
.setFirstRow(0)
.setMaxRows(10)
.findPagedList().getList();
Here is the expected SQL
SELECT * FROM
(SELECT /*+ FIRST_ROWS(10) */ rownum rn_, a.* FROM
(SELECT t0.id c0,
...
t0.OPTLOCK c32
FROM offer t0
INNER JOIN version t1 on t0.id = t1.OFFER_ID
ORDER BY t1.creation
) a
WHERE rownum <= 10
) ; --bind()
Here is the actual SQL
First one:
SELECT *
FROM
(SELECT
/*+ FIRST_ROWS(10) */
rownum rn_,
a.*
FROM
( SELECT DISTINCT t0.id c0,
...
t0.id
...
FROM offer t0
JOIN versions u1 ON u1.OFFER_ID = t0.id
WHERE u1.current_version = true
AND u1.current_version = true
ORDER BY t0.id
) a
WHERE rownum <= 10
) ;
Second one :
SELECT t0.OFFER_ID c0,
t0.id c1,
...
t0.creation c3,
...
t0.current_version c12,
...
t0.OFFER_ID c30,
...
FROM versions t0
WHERE (t0.OFFER_ID) IN (990,991,992,993,994,995,996,997,998,999)
ORDER BY t0.creation;
Question : What do I need to change to get the expected SQL ?
Thank you very much for help
Versions used :
ebean 7.6.1 from play 2.5.14
I found a walk-around:
Instead of
List<Offer> offers = query.fetch("versions")
.where()
.eq("versions.currentVersion", true)
.orderBy("versions.creation desc nulls last")
.setFirstRow(0)
.setMaxRows(10)
.findPagedList().getList();
I've found :
List<Offer> offers = query.fetch("versions")
.where()
.eq("versions.currentVersion", true)
.orderBy("u1.creation desc") // <-- line with the change
.setFirstRow(0)
.setMaxRows(10)
.findPagedList().getList();
I make sure that the join between offer and versions is made only once and than assume that version join alias will be u1.
At least, it's doning the job.
Bye
I want to get the list of all Branch even if they have no accounts with user role
Query query = em.createQuery("SELECT NEW com.package.BranchInstructors(b,a) FROM Branch b LEFT JOIN b.accounts a WHERE b.dFlg = 0 AND a.userRole = :role ORDER BY b.name ASC");
query.setParameter("role", "user");
return query.getResultList();
Unfortunately it's returning only Branches with user role, it's like doing INNER JOIN instead.
Any idea what's going on?
just add the a.userRole is null condition to your query to avoid filtering the null userRole that you got from the left join
SELECT NEW com.package.BranchInstructors(b,a)
FROM Branch b
LEFT JOIN b.accounts a
WHERE b.dFlg = 0
AND (a.userRole = :role OR a.userRole IS NULL)
ORDER BY b.name ASC"
The problem is in your WHERE vs LEFT JOIN clause.
If you use LEFT JOIN table Accounts and use this table in the WHERE with AND condition, it behaves like a JOIN.
So, you can use WITH in the LEFT JOIN:
Query query = em.createQuery("SELECT NEW com.package.BranchInstructors(b,a) FROM Branch b
LEFT JOIN b.accounts a WITH a.userRole = :role
WHERE b.dFlg = 0 ORDER BY b.name ASC");
Please bear with on this one. I have three RDDs ( coming from Hadoop ). All Three have unique keys such as ippaddress and boxnumber on which they can be matched/joined. Here are some sample data from all tables. Table A column boxnumber have to converted to number before it can be matched.
Table A:
ipaddress|boxnumber|cardnumber
94.254.57.16|59774DEa1|0D1EDF40
94.154.57.176|5F7377Ga9|0D3F796D
Table B:
cardno,boxnumber
1500914,2000096
1500413,2211469
Table C:
ipaddress|kanal|bitrate|kanaltimespent|date|country
94.254.57.16|sky|2023|003DF6A.ts|12-02-2016|chile
94.154.57.176|ITV|3425|003DF6A.ts|23-04-2014|egypt
My first attempt in java:
//TABLE A
JavaSparkContext sc = SetupSparkContext("SparkSample");
JavaRDD<ExtractTable_A> ta_RDD= ExtractTable_A.getRDD(sc);
JavaPairRDD<String, ExtractTable_A> A_PairRDD = ta_RDD.mapToPair(new PairFunction<extractTable_A, String, ExtractTable_A>()
{
#Override
public Tuple2<String, ExtractTable_A> call(ExtractTable_A extractTable_A) throws Exception
{
String [] A= extractTable_A.toString().split("|") ;
return new Tuple2<>(A[0],extractTable_A);
}
});
//TABLE B
JavaRDD<ExtractOttPdl> tb_RDD = ExtractTableB.getRDD(sc);
JavaPairRDD<String, ExtractTable_B> BPairRDD = tb_RDD.mapToPair(new PairFunction<extractTable_B, String, ExtractTable_B>()
{
#Override
public Tuple2<String, ExtractTable_B> call(ExtractTable_B extractTable_B) throws Exception
{
String [] B= extractTable_B.toString().split(",") ;
return new Tuple2<>(B[1],extractTable_B);
}
});
//TABE C
JavaRDD<ExtractTable_C> tc_RDD = ExtractTableC.getRDD(sc);
JavaPairRDD<String, ExtractTable_C> CPairRDD = tb_RDD.mapToPair(new PairFunction<extractTable_C, String, ExtractTable_C>()
{
#Override
public Tuple2<String, ExtractTableC> call(ExtractTableC extractTable_C) throws Exception
{
String [] C= extractTable_A.toString().split("|") ;
return new Tuple2<>(C[0],extractTable_A);
}
});
//At this point i need to join and create an .txt output file
The final result shoud be a file with these headers
KANAL|BITRATE|TIMESPENT|DATE|COUNTRY
===update===
I have managed to join the Table A and Table B but now i am stuck on how to join the TableC to Table A?
//Joined table A and B
JavaPairRDD<String, Tuple2<ExtractTableA, ExtractTableB>> join_1 = A_PairRDD.join(B_PairRDD);
. . .
//Joined table A and C
JavaPairRDD<String, Tuple2<ExtractTableA, ExtractTableC>> Join_2 = A_PairRDD.join(B_PairRDD);
// Output results from TableA and TableB
join_1.map(in -> {
return new ResultStringBuilder("|")
.append(Long.parseLong((in._2()._1().getCardno().trim()),16))
.append(Long.parseLong((in._2()._1().getBoxno().trim()),16))
.append(in._2()._2().getBoxno())
*** HERE I NEED TO ALSO APPEND THE COLUMN FROM TableC
.toString();
})
.saveAsTextFile("c:\outfile");
Remember that when you are working with the spark API, you always want to create a new RDD when you modify anything in the RDD sturcture because RDD is immutable.
In order to do three way join in this case,
you need to create a new JavaPairRDD after you join the first two tables,
because you want to have a PairRDD with new key-value pair because unique keys for Table A, B, C are different.
There could be two ways to do this (either join AB first or AC first)
The way you could join tables is like this:
Table A - Table B (PairRDD with key : boxnumber or cardnumber or maybe both)
After you join Table A and Table B, you need to create a new PairRDD with key ipaddress because you want to join with Table C.
// joinedAB is RDD resulting from join operation of Table A and C
JavaPairRDD joinedABForC = joinedAB.map(l -> new Tuple2(l[0], l));
// now joinedABForC has ipaddress as the RDD's key
// join rdd joinedABForC with Table C
After we moved the unique column to the key of the pairRdd, you can now join it with the Table C and three way join is done.
Joined Table AB - Table C (PairRdd with key : ipaddress)
How to change this complex sql statement into JPQL?
select a.name, a.surname, b.street, c.location, c.location_desc
from table1 join table2 on b.id = a.some_fk
left join table3 d on d.id = a.id
left join table4 c on c.some2_id = d.some2_fk where a.id = 400;
If this is possible to present in the JPQL form?
Impossible to give a definitive answer without knowing the entities and their mapping, but the query would look like this:
select a.name, a.surname, b.street, c.location, c.locationDesc
from Entity1 a
join a.entity2 b
left join a.entity3 d
left join d.entity4 c
where a.id = 400;
provided the necessary associations between entities are there.
JPQL is object oriented, it operates against JPA entity objects ,not database tables. Either you need to change the Question and add an UML diagramm or provide the Entity classes.