Hibernate #Formula recursive query with matching table - sql

I have a table with a ManyToMany relation with itself.
So there are two tables in my H2-Database:
supporting_asset: supporting_asset_dependencies:
id | provided_csc dependencies_id | supporting_assets_id
------------------ ---------------------------------------
1 | A1 1 | 2
2 | A3 1 | 3
3 | A2
I have a calculated attribute 'minCSC' and to calculate it I use the #Formula annotation:
#Formula(value="(Select min(sa.provided_csc) from supporting_asset_dependencies sad right join supporting_asset sa on sa.id = sad.dependencies_id where sad.supporting_assets_id = id group by id)")
This works fine, but depending assets can have dependencies of them own. With this I get a multileveled dependency tree. My goal is to get the minimum csc from this tree.
I tried:
WITH RECURSIVE tree(id, provided_csc, dep_id)
AS (SELECT sa.id, sa.provided_csc, sad.supporting_assets_id
FROM supporting_asset_dependencies AS sad
JOIN supporting_asset AS sa
ON sa.id=sad.dependencies_id
WHERE sad.supporting_assets_id=2
UNION ALL
SELECT child.id, child.provided_csc, childd.supporting_assets_id
FROM supporting_asset_dependencies AS childd
JOIN supporting_asset AS child
ON child.id=childd.dependencies_id
JOIN tree ON childd.supporting_assets_id=tree.id )
SELECT min(provided_csc)
FROM tree
but I get a 'Syntax error in SQL statement'. It seems like the formula is computed into:
(SUPPORTING2_.WITH[*] TREE(SUPPORTING2_.ID, SUPPORTING2_.PROVIDED_CSC, SUPPORTING2_.DEP_ID) AS ( ..
.. ) SELECT MIN(SUPPORTING2_.PROVIDED_CSC) FROM TREE) AS FORMULA0_1_,
It looks like it does not know the 'with recursive' command and tries to find it as field of the table.
How do I have to change the query to make it work or is there another way to achieve what I want?
---UPDATE---
I changed the query a little bit and on DB Fiddle it works for a SQLite DB. It also seems to work in the h2 web-console.

Related

Aggregating or Bundle a Many to Many Relationship in SQL Developer

So I have 1 single table with 2 columns : Sales_Order called ccso, Arrangement called arrmap
The table has distinct values for this combination and both these fields have a Many to Many relationship
1 ccso can have Multiple arrmap
1 arrmap can have Multiple ccso
All such combinations should be considered as one single bundle
Objective :
Assign a final map to each of the Sales Order as the Largest Arrangement in that Bundle
Example:
ccso : 100-10015 has 3 arrangements --> Now each of those arrangements have a set of Sales Orders --> Now those sales orders will also have a list of other arrangements and so on
(Image : 1)
Therefore the answer definitely points to something recursively checking. Ive managed to write the below code / codes and they work as long as I hard code a ccso in the where clause - But I don't know how to proceed after this now. (I'm an accountant by profession but finding more passion in coding recently) I've searched the forums and web for things like
Recursive CTEs,
many to many aggregation
cartesian product etc
and I'm sure there must be a term for this which I don't know yet. I've also tried
I have to use sqldeveloper or googlesheet query and filter formulas
sqldeveloper has restrictions on on some CTEs. If recursive is the way I'd like to know how and if I can control the depth to say 4 or 5 iterations
Ideally I'd want to update a third column with the final map if possible but if not, then a select query result is just fine
Codes I've tried
Code 1: As per Screenshot
WITH a1(ccso, amap) AS
(SELECT distinct a.ccso, a.arrmap
FROM rg_consol_map2 A
WHERE a.ccso = '100-10115' -- this condition defines the ultimate ancestors in your chain, change it as appropriate
UNION ALL
SELECT m.ccso, m.arrmap
FROM rg_consol_map2 m
JOIN a1
ON M.arrmap = a1.amap -- or m.ccso=a1.ccso
) /*if*/ CYCLE amap SET nemap TO 1 /*else*/ DEFAULT 0
SELECT DISTINCT amap FROM (SELECT ccso, amap FROM a1 ORDER BY 1 DESC) WHERE ROWNUM = 1
In this the main challenge is how to remove the hardcoded ccso and do a join for each of the ccso
Code 2 : Manual CTEs for depth
Here again the join outside the CTE gives me an error and sqldeveloper does not allow WITH clause with UPDATE statement - only works for select and cannot be enclosed within brackets as subtable
SELECT distinct ccso FROM
(
WITH ar1 AS
(SELECT distinct arrmap
FROM rg_consol_map
WHERE ccso = a.ccso
)
,so1 AS
(SELECT DISTINCT ccso
FROM rg_consol_map
WHERE arrmap IN (SELECT arrmap FROM ar1)
)
,ar2 AS
(SELECT DISTINCT ccso FROM rg_consol_map
where arrmap IN (select distinct arrmap FROM rg_consol_map
WHERE ccso IN (SELECT ccso FROM so1)
))
SELECT ar1.arrmap, NULL ccso FROM ar1
union all
SELECT null, ar2.ccso FROM ar2
UNION ALL
SELECT NULL arrmap, so1.ccso FROM so1
)
Am I Missing something here or is there an easier way to do this? I read something about MERGE and PROC SQL JOIN but was unable to get them to work but if that's the way to go ahead I will try further if someone can point me in the direction
(Image : 2)
(CSV File : [3])
Edit : Fixing CSV file link
https://github.com/karan360note/karanstackoverflow.git
I suppose can be downloaded from here IC mapping many to many.csv
Oracle 11g version is being used
Apologies in advance for the wall of text.
Your problem is a complex, multi-layered Many-to-Many query; there is no "easy" solution to this, because that is not a terribly ideal design choice. The safest best does literally include multiple layers of CTE or subqueries in order to achieve all the depths you want, as the only ways I know to do so recursively rely on an anchor column (like "parentID") to direct the recursion in a linear fashion. We don't have that option here; we'd go in circles without a way to track our path.
Therefore, I went basic, and with several subqueries. Every level checks for a) All orders containing a particular ARRMAP item, and then b) All additional items on those orders. It's clear enough for you to see the logic and modify to your needs. It will generate a new table that contains the original CCSO, the linking ARRMAP, and the related CCSO. Link: https://pastebin.com/un70JnpA
This should enable you to go back and perform the desired updates you want, based on order # or order date, etc... in a much more straightforward fashion. Once you have an anchor column, a CTE in the future is much more trivial (just search for "CTE recursion tree hierarchy").
SELECT DISTINCT
CCSO, RELATEDORDER
FROM myTempTable
WHERE CCSO = '100-10115'; /* to find all orders by CCSO, query SELECT DISTINCT RELATEDORDER */
--WHERE ARRMAP = 'ARR10524'; /* to find all orders by ARRMAP, query SELECT DISTINCT CCSO */
EDIT:
To better explain what this table generates, let me simplify the problem.
If you have order
A with arrangements 1 and 2;
B with arrangement 2, 3; and
C with arrangement 3;
then, by your initial inquiry and image, order A should related to orders B and C, right? The query generates the following table when you SELECT DISTINCT ccso, relatedOrder:
+-------+--------------+
| CCSO | RelatedOrder |
+----------------------+
| A | B |
| A | C |
+----------------------+
| B | C |
| B | A |
+----------------------+
| C | A |
| C | B |
+-------+--------------+
You can see here if you query WHERE CCSO = 'A' OR RelatedOrder = 'A', you'll get the same relationships, just flipped between the two columns.
+-------+--------------+
| CCSO | RelatedOrder |
+----------------------+
| A | B |
| A | C |
+----------------------+
| B | A |
+----------------------+
| C | A |
+-------+--------------+
So query only CCSO or RelatedOrder.
As for the results of WHERE CCSO = '100-10115', see image here, which includes all the links you showed in your Image #1, as well as additional depths of relations.

SQL Query to Show When Golfer Not Attached to an Event/Year

I am working on a school assignment that has downright stumped me for days. The task is to, using a view (VAvailableGolfers), populate a list box with Golfers who are not tied to a given event/year selected from a combo box. Here is the data in the tables:
The expected output on the form, then, would be:
2015 shows Goldstein available
2016 shows no one available
2017 shows both Goldstein and Everett available
so, in other words, where there isn't a record in TGolferEventYears for a golfer for a particular year
I have tried left joins, full outer joins, exists, not in, not exists, etc and I cannot seem to nail down the SQL to make it happen.
Here is the VB Form and the SQL backing it. I cannot figure out what to code in the view:
"SELECT intGolferID, strLastName FROM vAvailableGolfers WHERE intEventYearID = " & cboEvents.SelectedValue.ToString
Here is the view, which I know isn't giving correct output:
select tg.intGolferID, strLastName, intEventYearID
from TGolferEventYears TGEY, TGolfers TG
Where tgey.intGolferID = tg.intGolferID
and intEventYearID not IN
(select intEventYearID
from TEventYears
where intEventYearID not in
(select intEventYearID
from TGolferEventYears))
Appreciate any help
I usually approach this type of question by using a cross join to generate all possibly combination and then a left join/where to filter out the ones that already exist:
select g.intGolferID, g.strLastName, ey.intEventYearID
from TEventYears ey cross join
TGolfers g left join
TGolferEventYears gey
on gey.intGolferID = g.intGolferID and
gey.intEventYearID = ey.intEventYearID
where gey.intGolferID is null;
Try this query:
SELECT tg.intGolferID, strLastName, tey.intEventYearID, tey.intEventYear
FROM TGolfers tg, TEventYears tey
WHERE tg.intGolferID NOT IN (
SELECT DISTINCT tgey.intGolferID
FROM TGolferEventYears tgey
WHERE tgey.intEventYearID = tey.intEventYearID
)
Explanation
Since you are trying to get combinations of data that is not in TGolferEventYears, you cannot use it in your outer-most SELECT as any of its columns would be NULL. Therefore, you need to SELECT FROM the tables that are the sources of that data, and going through each joined record, filter out the combinations that are in TGolferEventYears.
Main query
Select the data you need:
SELECT tg.intGolferID, strLastName, tey.intEventYearID, tey.intEventYear
...from TGolfers, cross join with TEventYears:
FROM TGolfers tg, TEventYears tey
...where the golfer ID does not exist in the following collection:
WHERE tg.intGolferID NOT IN ( ... )
Subquery
Select unique golfer IDs:
SELECT DISTINCT tgey.intGolferID
...from TGolferEventYears:
FROM TGolferEventYears tgey
...where the year is the current year of the outer query:
WHERE tgey.intEventYearID = tey.intEventYearID
Result
+-------------+-------------+----------------+--------------+
| intGolferID | strLastName | intEventYearID | intEventYear |
+-------------+-------------+----------------+--------------+
| 1 | Goldstein | 1 | 2015 |
| 1 | Goldstein | 3 | 2017 |
| 2 | Everett | 3 | 2017 |
+-------------+-------------+----------------+--------------+

Select conditionally bound columns from 2 tables into one line

I searched, but seems I haven't found the right keyword for describing what I am trying to achieve, so please be lenient if this is a known problem, just point me to the right keywords.
I have following tables/entries:
select * from personnes where id=66;
id | aclid | referendid | login | validated | passwd
66 | | | toto#tiiti.com | f | $2y$10$w3DRh/g2Tebu/mkMcQz32OUB.dDjFiBP99vWlMrrPWpR45JZDdw4W
and
select * from pattributs where (name='nom' OR name='prenom') AND persid=66;
id | name | value | persid
----+--------+-------+--------
90 | prenom | Jean | 66
91 | nom | Meyer | 66
Now I use that form for not cluttering the main table, since depending on the case, I record the name, or not....
but having a view as a table of the completed table would be nice, so I tried:
select (personnes."id","login",
(select "value" from pattributs where "name"='nom' AND "persid"=66),
(select "value" from pattributs where "name"='prenom' AND "persid"=66)
) from personnes where personnes.id=66;
which seems to do the job:
row
--------------------------------
(66,toto#tiiti.com,Meyer,Jean)
but the column tags disappeared, and being able to fetch them from the invoking php script is immensely useful, but when I add:
select (personnes."id","login",
(select "value" from pattributs where "name"='nom' AND "persid"=66),
(select "value" from pattributs where "name"='prenom' AND "persid"=66) as 'prenom')
from personnes where personnes.id=66;
I get a syntax error at the as directive... So probably I haven't understood how to do this properly, the braces indicate that this isn't anymore in tabular form), so how can I achieve the following result:
id | login | nom | prenom
66 |toto#tiiti.com | Meyer | Jean
The idea being to store a suitable view for each use case, bundling only the relevant columns.
Thanks in advance
To answer your question: You are loosing the column names because you are creating a simple data set, a row. This is done by your braces. Without them you should get your expected result.
But your solution is not very well: You should avoid to calculate your single columns in every single subquery. This could be done easily in one SELECT:
select
pe.id,
pe.login,
MIN(pa.value) FILTER (WHERE pa.name = 'nom') as nom,
MIN(pa.value) FILTER (WHERE pa.name = 'prenom') as prenom
from
personnes pe
join pattributs pa ON pe.id = pa.persid AND pe.id = 66
where pa.name = 'nom' or pa.name = 'prenom'
group by pe.id, pe.login
First you'll need a JOIN to get the right datasets of both tables together. You should join on the id.
Then you have the problem that you have two rows for the name (which seems not very well designed, why not two columns?). These two values can be grouped by the id. Now you could aggregate them.
What I am doing is to "aggregate" them (it doesn't matter what function I am using because it should be only one value). The FILTER clause filters out the right value.

Using nested queries:SQL

I am trying to store the following data in a table where the visible_to is a multivalued attribute.
Wall_ID Facebook_ID Visible_To
W1 F1 F2,F3,F4
W2 F2 F1
W3 F3 F1
W4 F4 F1
I am trying to emulate Facebook on Oracle. I want to find the user who can view the max no of other's wall(here:F1).
I have gotten to the point of storing the multivalued attribute using NESTED table in Oracle 11g. Do I have to un-nest the table to find the result of the query or is there another way to do it?
Thanks!
If I'm understanding what you're trying to do correctly, then something like this should work (Please see the SQL Fiddle):
SELECT *
FROM (
SELECT
f.wall_id
, f.facebook_id
, COUNT(t.COLUMN_VALUE) visible_to_count
FROM facebook_data f
CROSS JOIN TABLE(f.visible_to) t
GROUP BY f.wall_id, f.facebook_id
ORDER BY visible_to_count DESC
)
WHERE ROWNUM = 1
Results:
| WALL_ID | FACEBOOK_ID | VISIBLE_TO_COUNT |
--------------------------------------------
| W1 | F1 | 3 |
This simply unfolds the nested table and then aggregates so you can count up the number of values. You might also want to consider adding the DISTINCT keyword to the COUNT() function if you're likely to store duplicate values, or otherwise introduce cardinality in the query.

Access join on first record

I have two tables in an Access database, tblProducts and tblProductGroups.
I am trying to run a query that joins both of these tables, and brings back a single record for each product. The problem is that the current design allows for a product to be listed in the tblProductGroups table more than 1 - i.e. a product can be a member of more than one group (i didnt design this!)
The query is this:
select tblProducts.intID, tblProducts.strTitle, tblProductGroups.intGroup
from tblProducts
inner join tblProductGroups on tblProducts.intID = tblProductGroups.intProduct
where tblProductGroups.intGroup = 56
and tblProducts.blnActive
order by tblProducts.intSort asc, tblProducts.curPrice asc
At the moment this returns results such as:
intID | strTitle | intGroup
1 | Product 1 | 1
1 | Product 1 | 2
2 | Product 2 | 1
2 | Product 2 | 2
Whereas I only want the join to be based on the first matching record, so that would return:
intID | strTitle | intGroup
1 | Product 1 | 1
2 | Product 2 | 1
Is this possible in Access?
Thanks in advance
Al
This option runs a subquery to find the minimum intGoup for each tblProducts.intID.
SELECT tblProducts.intID
, tblProducts.strTitle
, (SELECT TOP 1 intGroup
FROM tblProductGroups
WHERE intProduct=tblProducts.intID
ORDER BY intGroup ASC) AS intGroup
FROM tblProducts
WHERE tblProducts.blnActive
ORDER BY tblProducts.intSort ASC, tblProducts.curPrice ASC
This works for me. Maybe this helps someone:
SELECT
a.Lagerort_ID,
FIRST(a.Regal) AS frstRegal,
FIRST(a.Fachboden) AS frstFachboden,
FIRST(a.xOffset) AS frstxOffset,
FIRST(a.yOffset) AS frstyOffset,
FIRST(a.xSize) AS frstxSize,
FIRST(a.ySize) AS frstySize,
FIRST(a.Platzgr) AS frstyPlatzgr,
FIRST(b.Artikel_ID) AS frstArtikel_ID,
FIRST(b.Menge) AS frstMenge,
FIRST(c.Breite) AS frstBreite,
FIRST(c.Tiefe) AS frstTiefe,
FIRST(a.Fachboden_ID) AS frstFachboden_ID,
FIRST(b.BewegungsDatum) AS frstBewegungsDatum,
FIRST(b.ErzeugungsDatum) AS frstErzeugungsDatum
FROM ((Lagerort AS a)
LEFT JOIN LO_zu_ART AS b ON a.Lagerort_ID = b.Lagerort_ID)
LEFT JOIN Regal AS c ON a.Regal = c.Regal
GROUP BY a.Lagerort_ID
ORDER BY FIRST(a.Regal), FIRST(a.Fachboden), FIRST(a.xOffset), FIRST(a.yOffset);
I have non unique entries for Lagerort_ID on the table LO_zu_ART. My goal was to only use the first found entry from LO_zu_ART to match into Lagerort.
The trick is to use FIRST() an any column but the grouped one. This may also work with MIN() or MAX(), but I have not tested it.
Also make sure to call the Fields with the "AS" statement different than the original field. I used frstFIELDNAME. This is important, otherwise I got errors.
Create a new query, qryFirstGroupPerProduct:
SELECT intProduct, Min(intGroup) AS lowest_group
FROM tblProductGroups
GROUP BY intProduct;
Then JOIN qryFirstGroupPerProduct (instead of tblProductsGroups) to tblProducts.
Or you could do it as a subquery instead of a separate saved query, if you prefer.
It's not very optimal, but if you're bringing in a few thousand records this will work:
Create a query that gets the max of tblProducts.intID from one table and call it qry_Temp.
Create another query and join qry_temp to the table you are trying to join against, and you should get your results.