JPA discriminator from joined table - jpa

I need to map a subset of table A to an entity. Unfortunatelly, the discriminator in an other table B.
Is it possilbe to specify such an entity with annotations so a simple select without any conditions returns only the desired subset?
Exampe:
Table Kingdom
id, name
1, animal
2, plant
Table Lifeform
id, name, Kingdom_Id
1, dog, 1
2, cat, 1
3, tree, 2
4, grass, 2
Using these tables as exmple, I would like to have an entity called Plant.
The jpql query "select p from Plant p" should produce the same result as
select * from Lifeform l join Kingdom k on l.Kingdom_Id = k.id and k.name = 'plant';
I tried to accomplish this by using #DiscriminatorColumn, #Inheritance and #DiscriminatorValue annotations. But I think it is not possible that way.

Related

PostgreSQL: recursively join a second table

I'm struggling with recursion in PostgreSQL. I need to join a first table with a second one, and then recursively join within the second table. I looked at quite a number of examples, but most are about finding the parent records within a single table, and this has left me utterly confused.
Here's a minimal example with tables thing and category. Records in thing may or may not have a category:
id
name
category
1
a5
3
2
passat
2
3
apple
NULL
Records in category may have one or more parents in the same table:
id
name
parent_category
1
vehicle
NULL
2
car
1
3
coupe
2
The result I'm looking for is the combination of all things with their categories, as well as the category level (1 for the direct parent, 2 for the level above).
thing_name
category_name
level
a5
coupe
1
a5
car
2
a5
vehicle
3
passat
car
1
passat
vehicle
2
apple
NULL
NULL
I have a DB Fiddle here: https://www.db-fiddle.com/f/b7V8ddragZZ9x2RsMkdFYn/5
CREATE TABLE category (
id INT,
name TEXT,
parent_category INT
);
INSERT INTO category VALUES (1, 'vehicle', null);
INSERT INTO category VALUES (2, 'car', 1);
INSERT INTO category VALUES (3, 'coupe', 2);
CREATE TABLE thing (
id INT,
name TEXT,
category INT
);
INSERT INTO thing VALUES (1, 'a5', 3);
INSERT INTO thing VALUES (2, 'passat', 2);
INSERT INTO thing VALUES (3, 'apple', null);
Use a CTE to join the tables, giving you a tree-like view of combined thing_categories, which you can then use with a normal recursive CTE.
with recursive join_thing_category as (
select thing.id as thing_id,
thing.name as thing_name,
thing.category as thing_category,
category.id as category_id,
category.name as category_name,
category.parent_category as parent_category
from thing left join category on thing.category=category.id
),
recursive_part(n) as (
select thing_id, thing_name, thing_category, category_id, category_name, parent_category, (0*parent_category) + 1 as level from join_thing_category
union all
select 1, thing_name, thing_category, cat.id category_id, cat.name, cat.parent_category as parent, level+1 as level from recursive_part rp cross join category cat
where cat.id=rp.parent_category
)
select thing_name, category_name, level from recursive_part order by 1, 2, 3 limit 1024;
thing_name
category_name
level
a5
car
2
a5
coupe
1
a5
vehicle
3
apple
passat
car
1
passat
vehicle
2
View on DB Fiddle
The (0*parent_category) + 1 as level bit is so that things with no category get NULL as their level instead of 1.

SQL SELECT Parent-Child with SORT (...again)

UPDATED:
I have a simple, one level parent child relation table, with following columns:
ID_Asset| Parent_ID_Asset | ProductTitle
I need output grouped by Parent followed by children, and also sorted by Parent and Children Name. My attempts in the fiddle.
See here for details: https://rextester.com/PPCHG20007
Desired order:
9 8 NULL ADONIS Server
7 16 8 ADONIS Designer
8 20 8 ADONIS Portal Module “Control & Release” Package XS
Parent first, than children, while ProductTitle ordered alphabetically.
Thanx all for hints so far.
I would do conditional ordering instead :
select t.*
from table t
order by (case when parent_id is null then id else parent_id end), ProductTitle;
I am assuming you need to sort the data based on parent-child relation.
As far as I understand, you need to sort on the name of the root products, and in between them to show the sub-products, ordered by name, and in between them to show their sub-products, etc.
I guess you are using a recursive cte. You can define a "hierarchy sorting" helper which is a padded number in the current level, and for each level deep, add a suffix with the padded number in the current level, etc.
Something like that:
declare #Products table(ID int, Parent_ID int, ProductTitle varchar(100))
insert into #Products values
(1, NULL, 'ADONIS'),
(2, NULL, 'BACARAT'),
(3, 1, 'Portal Module'),
(4, 1, 'Alhambra'),
(5, NULL, 'ZULU'),
(6, 2, 'Omega')
; with cte as (
select ID, Parent_ID, ProductTitle, FORMAT(ROW_NUMBER() over(order by ProductTitle), '0000') as SortingHelper
from #Products
where Parent_ID is null
union all
select p.ID, p.Parent_ID, p.ProductTitle, cte.SortingHelper + '.' + FORMAT(ROW_NUMBER() over(order by p.ProductTitle), '0000') as SortingHelper
from #Products p
inner join cte on cte.ID = p.Parent_ID
)
select ID, Parent_ID, ProductTitle
from cte
order by SortingHelper
I think this is the ordering you are looking for. This joins each row to its parent (if it exists). Then if there is a TP (parent) record then that is the parent title, ID etc, otherwise the current record must be the parent. I've shown the parent name in the query results for clarity. Then it sorts by
Parent name (so parents and children are together, but in parent name order)
Parent ID (in case two or more parents have the same name/title, this will keep children with the correct parent)
A flag which is 0 for parents, 1 for children, so the parent comes first
The current record name, which will sort children by name/title order
Code is
Select T.*,
isnull(TP.ProductTitle, T.ProductTitle) as ParentName -- This is the parent name, shown for reference
from test T
left outer join Test TP on TP.ID_Asset = T.Parent_ID_Asset --This is the parent record, if it exists
ORDER BY isnull(TP.ProductTitle, T.ProductTitle), --ParentName sort
isnull(TP.ID_Asset, T.ID_Asset), --if two parents have the same title, this makes sure they group with their correct children
Case when T.Parent_ID_Asset is null then 0 else 1 end, --this makes sure the parent comes before the child
T.ProductTitle --Child Sort

SQL for joining 2 tables but a bit complicated for my understanding

Got a situation thats a bit beyond my understanding.
Table A has the Product, Country and Factory
Table B has the Product, Factory and city.
The scenario is such that sales forecast data flows from the country level via the factory and then to city level. We have factories only in Rotterdam and Amsterdam. The issue is such that the factories in Table A need to be the same as the factory in table B.
I have to clean data for situations C&D where the factories in Table A are wrong and need cleaning. I therefore first need to identify these wrong records:
Here is what I got so far by joining Table A and B
select A.Prod,A.country,A.factory,B.Prod,B.factory,B.City from Table1 A, Table2 B where and A.Prod=B.Prod and A.Factory <>B.Factory
Of course I can find a specific known wrong record by using below SQL, but I need to find for all wrong records without specifying any product or
select A.Prod,A.country,A.factory,B.Prod,B.factory,B.City from Table1 A, Table2 B where A.Prod=B.Prod and A.Factory <>B.Factory
and A.Country ='Norway' and A.Factory ='Rotterdam' and B.City ='Oslo'
Situation 1
Table A
Product Country Factory
ProdA Switzerland Rotterdam
Table B
Product Factory City
ProdA Rotterdam Geneva
Situation 2
Table A
Product Country Factory
Prod Germany Rotterdam
Table B
Product Factory City
ProdB Rotterdam Dresden
Situation 3
Table A
Product Country Factory
ProdC Norway Rotterdam
Table B
Product Factory City
ProdC Amsterdam Oslo
Situation 4
Table A
Product Country Factory
ProdD Finland Rotterdam
Table B
Product Factory City
ProdD Amsterdam Helsinki
From what I understand Your projection for the country in Table A, has to be for a city in table B, which exists in the country in table A.
So, in situation 1, we have
Table A country = Switzerland, Table B city = Geneva.
Since, Geneva is in Switzerland, this is fine
In situation 2, we have
Table A country = Germany, Table B city = Dresden
Since Dresden is in Germany this is fine.
This gives us a clue on how we can attack the problem.
Step 1. Setup a table for your expected country/city
CREATE TABLE COUNTRY_CITY (COUNTRY VARCHAR(60), CITY VARCHAR(60));
STEP 2. Insert the values for expected country/city into table
INSERT INTO COUNTRY_CITY(COUNTRY,CITY) VALUES('GERMANY','DRESDEN');
INSERT INTO COUNTRY_CITY(COUNTRY,CITY) VALUES('SWITZERLAND','GENEVA');
INSERT INTO COUNTRY_CITY(COUNTRY,CITY) VALUES('NORWAY','OSLO');
INSERT INTO COUNTRY_CITY(COUNTRY,CITY) VALUES('FINLAND','HELSINKI');
STEP 3.
select A.Prod,A.country, A.factory, B.Prod, B.factory, B.City,
COUNTRY_CITY.CITY
from
Table1 A
INNER JOIN Table2 B ON A.Prod=B.Prod
INNER JOIN COUNTRY_CITY ON A.COUNTRY = COUNTRY_CITY.COUNTRY
where COUNTRY_CITY.CITY = B.city and A.Factory <> B.Factory
So in step 3, we give the database the knowledge of which city belongs to which country, so that we can do a join from Table A to table B. Once you get that, then the condition on the not matching factories should be the records that you are looking for

Full outer join on multiple tables in PostgreSQL

In PostgreSQL, I have N tables, each consisting of two columns: id and value. Within each table, id is a unique identifier and value is numeric.
I would like to join all the tables using id and, for each id, create a sum of values of all the tables where the id is present (meaning the id may be present only in subset of tables).
I was trying the following query:
SELECT COALESCE(a.id, b.id, c.id) AS id,
COALESCE(a.value,0) + COALESCE(b.value,0) + COALESCE(c.value.0) AS value
FROM
a
FULL OUTER JOIN
b
ON (a.id=b.id)
FULL OUTER JOIN
c
ON (b.id=c.id)
But it doesn't work for cases when the id is present in a and c, but not in b.
I suppose I would have to do some bracketing like:
SELECT COALESCE(x.id, c.id) AS id, x.value+c.value AS value
FROM
(SELECT COALESCE(a.id, b.id), a.value+b.value AS value
FROM
a
FULL OUTER JOIN
b
ON (a.id=b.id)
) AS x
FULL OUTER JOIN
c
ON (x.id = c.id)
It was only 3 tables and the code is ugly enough already imho. Is there some elegant, systematic ways how to do the join for N tables? Not to get lost in my code?
I would also like to point out that I did some simplifications in my example. Tables a, b, c, ..., are actually results of quite complex queries over several materialized views. But the syntactical problem remains the same.
I understood you need to sum the values from N tables and group them by id, correct?
For that I would do this:
Select x.id, sum (x.value) from (
Select * from a
Union all
Select * from b
Union all........
) as x group by x.id;
Since the n tables are composed by the same fields you can union them all creating a big table full of all the id - value tuples from all tables. Use union all because union filters for duplicates!
Then just sum all the values grouped by id.

postgres query (more than function)

Let's assume that I have 2 tables: Human, Car.
Table Car has some columns and one of them is Human_id. For example, like this:
Human_id Type
1 BMW
5 Mercedes
1 Audi
2 Peugeot
3 BMW
2 JEEP
How can I select all humans who have more than one car? I mean I need to control if Human_id is used more than once in the Human_id column. But how I can do that?
SELECT human.name
FROM Human, Car
WHERE .... <- what goes here??
SELECT human.name, car.human_id, count(*) as total
FROM Human, Car
group by human.name, car.human_id
having count(*) > 1
Try grouping your Car records by Human_id, and take a look at the HAVING clause.
If you have a FK between Human and Car tables, it's best to use a Join instead of a Cartesian product:
create table Human (
idHuman integer primary key,
name varchar(15),
...
);
insert into Human (idHuman, name) values
(1,'A'), (2,'B'), (3,'C'), (4,'D'), (5,'E');
create table Car (
Human_id integer REFERENCES Human ON DELETE CASCADE,
Type varchar(15),
...
);
insert into Car (Human_id, Type) values
(1,'BMW'), (5,'Mercedes'), (1,'Audi')
,(2,'Peugeot'), (3,'BMW'), (2,'JEEP');
And the query:
select hu.name
from Human hu
join Car ca on (ca.Human_id = hu.idHuman)
group by hu.name
having count(*) > 1
I hope that I help you
select human.name
from human
where human.id in
select human.id
from car
where count(car) > 1
group by human.id