How to integrate WITH Recursive in a Querydsl query - recursive-query

I'm new to Querydsl and I'm struggling to figure out how to implement the following query:
WITH RECURSIVE results AS
(
SELECT id,
parent_id
FROM project
WHERE id = '8a3d6714-27fa-4d1f-962f-9047d616ab42'
UNION ALL
SELECT t.id,
t.parent_id
FROM project t
INNER JOIN results r ON r.parent_id = t.id
)
SELECT *
FROM results;
Its objective is to, starting from the bottom of a hierarchy, collect the anchor's parent (8a3d6714-27fa-4d1f-962f-9047d616ab42 in the above example; there is only one parent per row) all the way up to a point where there is a element without a parent. If successful, using my database, it should yield the following:
id parent_id
------------------------------------ ------------------------------------
8a3d6714-27fa-4d1f-962f-9047d616ab42 babc74e8-1b6f-49e8-a1e3-a176fce2975d
babc74e8-1b6f-49e8-a1e3-a176fce2975d 3f83c9a2-bf43-46d8-bf87-070f5b55ae5a
3f83c9a2-bf43-46d8-bf87-070f5b55ae5a 69c074c6-a329-42c3-8e2e-5da9ab0ef81e
69c074c6-a329-42c3-8e2e-5da9ab0ef81e bccab264-027c-4c4f-9ae8-3409efc6aeb3
bccab264-027c-4c4f-9ae8-3409efc6aeb3 227db39a-2219-4abb-a2a7-3d28bf47ac01
227db39a-2219-4abb-a2a7-3d28bf47ac01
Any thoughts would be much appreciated.
Regards
Rodrigo

QProjects results = new QProjects("results");
QProjects p = new QProjects("p");
QProjects t = new QProjects("t");
String id = "8a3d6714-27fa-4d1f-962f-9047d616ab42";
query.withRecursive(results, subQuery()
.unionAll(
subQuery().from(p).where(p.id.eq(id)).list(p.id, p.parentId),
subQuery().from(t).innerJoin(results).on(results.parentId.eq(t.id)).list(t.id, t.parentId)))
.from(results)
.list(results.id, results.parentId);

Related

Postgresql, how to SELECT json object without duplicated rows

I'm trying to find out how to get JSON object results of selected rows, and not to show duplicated rows.
My current query:
SELECT DISTINCT ON (vp.id) jsonb_agg(jsonb_build_object('affiliate',a.*)) as affiliates, jsonb_agg(jsonb_build_object('vendor',vp.*)) as vendors FROM
affiliates a
INNER JOIN related_affiliates ra ON a.id = ra.affiliate_id
INNER JOIN related_vendors rv ON ra.product_id = rv.product_id
INNER JOIN vendor_partners vp ON rv.vendor_partner_id = vp.id
WHERE ra.product_id = 79 AND a.is_active = true
GROUP BY vp.id
The results that I receive from this is:
[
affiliates: {
affiliate: affiliate1
affiliate: affiliate2
},
vendors: {
vendor: vendor1,
vendor: vendor1,
}
As you can see in the second record, vendor is still vendor1 because there are no more results, so I'd like to also know if there's a way to remove duplicates.
Thanks.
First point : the result you display here above doesn't conform the json type : the keys are not double-quoted, the string values are not double-quoted, having dupplicated keys in the same json object ('{"affiliate": "affiliate1", "affiliate": "affiliate2"}' :: json) is not be accepted with the jsonb type (but it is with the json type).
Second point : you can try to add the DISTINCT key word directly in the jsonb_agg function :
jsonb_agg(DISTINCT jsonb_build_object('vendor',vp.*))
and remove the DISTINCT ON (vp.id) clause.
You can also add an ORDER BY clause directly in any aggregate function. For more information, see the manual.
You could aggregate first, then join on the results of the aggregates:
SELECT a.affiliates, v.vendors
FROM (
select af.id, jsonb_agg(jsonb_build_object('affiliate',af.*)) as affiliates
from affiliates af
group by af.id
) a
JOIN related_affiliates ra ON a.id = ra.affiliate_id
JOIN related_vendors rv ON ra.product_id = rv.product_id
JOIN (
select vp.id, jsonb_agg(jsonb_build_object('vendor',vp.*)) as vendors
from vendor_partners vp
group by vp.id
) v ON rv.vendor_partner_id = v.id
WHERE ra.product_id = 79
AND a.is_active = true

Insert from several temporary tables

I want to rewrite the following query using jooq:
with first_temp as (
select a.id as lie_id
from first_table a
where a.some_Field = 100160
), second_temp as (
select b.id as ben_id
from second_table b
where b.email = 'some.email#gmail.com'
) insert into third_table (first_table_id, second_table_id)
select a.lie_id, b.ben_id from first_temp a, second_temp b;
I was trying something like the following:
DriverManager.getConnection(url, login, password).use {
val create = DSL.using(it, SQLDialect.POSTGRES)
create.with("first_temp").`as`(create.select(FIRST_TABLE.ID.`as`("lie_id")))
.with("second_temp").`as`(create.select(SECOND_TABLE.ID.`as`("ben_id")))
.insertInto(THIRD_TABLE, THIRD_TABLE.FIRST_TABLE_ID, THIRD_TABLE.SECOND_TABLE_ID)
.select(create.select().from("first_temp", "second_temp"), create.select().from("second_temp")))
}
But without success.
Your fixed query
// You forgot FROM and WHERE clauses in your CTEs!
create.with("first_temp").`as`(
create.select(FIRST_TABLE.ID.`as`("lie_id"))
.from(FIRST_TABLE)
.where(FIRST_TABLE.SOME_FIELD.eq(100160)))
.with("second_temp").`as`(
create.select(SECOND_TABLE.ID.`as`("ben_id"))
.from(SECOND_TABLE)
.where(SECOND_TABLE.EMAIL.eq("some.email#gmail.com")))
.insertInto(THIRD_TABLE, THIRD_TABLE.FIRST_TABLE_ID, THIRD_TABLE.SECOND_TABLE_ID)
// You had too many queries in this part of the statement, and
// didn't project the two columns you were interested int
.select(create.select(
field(name("first_temp", "lie_id")),
field(name("second_temp", "ben_id")))
.from("first_temp", "second_temp"))
// Don't forget this ;-)
.execute();
But frankly, why even use CTE at all? Your query would be much simpler like this, both in SQL and in jOOQ (assuming that you really want this cartesian product):
Better SQL Version
insert into third_table (first_table_id, second_table_id)
select a.id, b.id
from first_table a, second_table b
where a.some_field = 100160
and b.email = 'some.email#gmail.com';
Better jOOQ Version
create.insertInto(THIRD_TABLE, THIRD_TABLE.FIRST_TABLE_ID, THIRD_TABLE.SECOND_TABLE_ID)
.select(create.select(FIRST_TABLE.ID, SECOND_TABLE.ID)
.from(FIRST_TABLE, SECOND_TABLE)
.where(FIRST_TABLE.SOME_FIELD.eq(100160))
.and(SECOND_TABLE.EMAIL.eq("some_email#gmail.com")))
.execute();

Need help in creating CriteriaQuery

First of all, I would like to know if it is possible to do?
Below is my query and I am trying to build using criteria.
SELECT CONCAT('record-', rl.record_id) AS tempId,
'sloka' AS type,
rl.record_id AS recordId,
rl.title AS title,
rl.locale as locale,
rl.intro AS intro,
rl.title AS localetitle,
NULL AS audioUrl,
lp.name AS byName,
lp.person_id AS byId,
lp.name AS onName,
lp.person_id AS onId
FROM record_locale rl
LEFT JOIN record r ON rl.record_id = r.record_id
LEFT JOIN locale_person lp ON r.written_on = lp.person_id
WHERE rl.title LIKE :title
AND rl.locale = :locale
AND lp.locale = :locale
UNION
SELECT CONCAT('lyric-', s.song_id) AS tempId,
'bhajan' AS type,
s.song_id AS recordId,
s.title,
l.locale as locale,
NULL AS intro,
l.title AS localetitle,
s.audio_url AS audioUrl,
lpb.name AS byName,
lpb.person_id AS byId,
lpo.name AS onName,
lpo.person_id AS onId
FROM song s
LEFT JOIN locale_person lpb
ON (s.written_by = lpb.person_id AND lpb.locale = :locale)
LEFT JOIN locale_person lpo
ON (s.written_on = lpo.person_id AND lpo.locale = lpb.locale)
INNER JOIN lyric l
ON (l.locale = lpb.locale AND l.song_id = s.song_id)
WHERE s.title LIKE :title AND s.approved_by IS NOT NULL
ORDER BY localeTitle ASC
// END
Based on few conditions, I might need to have union of both queries or just individual query without union.
Converting the SQL to JPQL is usually a good first step, as we can't quite tell what these tables map to, or what you are expecting to get back. If it is possible to do in JPQL, it should be possible with a criteria query. Except in this case: JPA/JPQL does not have the union operator so it won't work in straight JPA, but some providers such as EclipseLink have support. See:
UNION to JPA Query
and
http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/j_union.htm

Why is this field not showing up in the results?

When I run the selects below, I do not get Field3 in the result set, why?
Select
a.Field1,
a.Field2,
a.Field3,
sum(IsNull(a.Field4, 0)) AS SomeAlias1,
a.SomeField5,
a.SomeField6,
a.SomeField7
From SomeTable a
INNER JOIN SomeView1 v on v.au = a.au
inner join (select Username, House from Users userBuildings where UserName = #UserName) as userHouses on userHouses.au = a.au
WHERE
(((where claus logic here....
Group BY a.Field1,
a.Field2,
a.SomeAlias1,
a.Field3,
a.Field4,
a.Field5,
a.Field6,
a.Fielf7
)
Select
transBudget.Field1,
transBudget.Field2,
transDiscount.Field4,
... some other fields...
IsNull(transDiscount.Actual, 0) - IsNull(transBudget.Actual, 0) AS Variance
from (Select * from Transactdions Where TransDesc = 'Budget') AS transBudget
FULL OUTER JOIN
(Select * from Transactions Where TransDesc = 'Discount') AS transDiscount
ON transBudget.Market = transDiscount.Market AND transBudget.SubMarket = transDiscount.SubMarket
I see every field except Field3 for some reason and it's beyond me how the heck this can happen.
In the second part of your query, you are missing field 3.
Select
transBudget.Field1,
transBudget.Field2,
transDiscount.Field4,
... some other fields...
IsNull(transDiscount.Actual, 0)
You appear to have two separate SQL queries there. The first one contains Field3, but the second one does not.

How to perform Linq to Entites Left Outer Join

I have read plenty of blog posts and have yet to find a clear and simple example of how to perform a LEFT OUTER JOIN between two tables. The Wikipedia article on joins Join (SQL) provides this simple model:
CREATE TABLE `employee` (
`LastName` varchar(25),
`DepartmentID` int(4),
UNIQUE KEY `LastName` (`LastName`)
);
CREATE TABLE `department` (
`DepartmentID` int(4),
`DepartmentName` varchar(25),
UNIQUE KEY `DepartmentID` (`DepartmentID`)
);
Assume we had a EmployeeSet as an employee container ObjectSet<Employee> EmployeeSet and a DepartmentSet ObjectSet<Department> DepartmentSet. How would you perform the following query using Linq?
SELECT LastName, DepartmentName
FROM employee e
LEFT JOIN department d
ON e.DepartmentID = d.DepartmentID
I would write this, which is far simpler than join and does exactly the same thing:
var q = from e in db.EmployeeSet
select new
{
LastName = e.LastName,
DepartmentName = e.Department.DepartmentName
};
You need to use the DefaultIfEmpty method :
var query =
from e in db.EmployeeSet
join d in db.DepartmentSet on e.DepartmentID equals d.DepartmentID into temp
from d in temp.DefaultIfEmpty()
select new { Employee = e, Department = d };