Recursive query using JPA to get employee hierarchy - mysql

I am trying to find a way to get the employee hierarchy in an organization using JPA and Spring boot.
The problem is similar to the following link
Similar Data Model
If A reports to B and B reports to C and C reports to D,
For employee B i need the output to be B->C->D.
The following the Recursive query in Mysql
WITH RECURSIVE Employee_path(id,Name,bid) AS(
SELECT id,Name,bid
FROM Employee
WHERE id = 1005
UNION ALL
SELECT e.id,e.Name,e.bid
FROM Employee_path AS cp JOIN Employee AS e
ON cp.bid = e.id
)
SELECT * FROM Employee_path ORDER by bid ASC;
Since I am new to JPA I am unable to get the solution in an optimised way(I am not getting how to approach this problem)
I just added a recursive way of getting it in an optimised way.
PFL for the repo
For my git repo
Consider following example as MySQL snapshot
id name bid
1 A 0
2 B 1
3 C 2
4 D 1
I want to get the Reporting manager line for person C
Which should return string C->B->A

Related

"Seek" paging with jOOQ skipping rows

I'm trying to implement "seek" paging using jOOQ (3.11.12) + MySQL (5.7.24).
I have a table of products, that contains the following rows:
ID | Name | Created At
---------------------- --------- -------------------
XjpPXlZxT5i3tTjO7lZQ6Q Product A 2019-10-25 03:23:05
SmytEB9lTW-UiVFhg_gViQ Product B 2019-10-09 05:43:44
glpNYcsBTJqAzQERbgGh5g Product C 2019-10-02 14:53:48
HDZ1K7g_Rj-2vdQaEj79Ow Product D 2019-09-07 14:52:56
aTcWWxdJSReZBGzkLXuNIQ Product E 2019-09-06 08:21:24
HPOD380mTR-g2Ut4Da0k4Q Product F 2019-09-06 08:19:57
jXzfHBDAQ6We4CjXLem_WA Product G 2019-09-06 08:16:06
duxiQ3InRXaFy_JVDkkewQ Product H 2019-09-06 08:15:02
QF-3ECfLQD2vdVGE_5X-rQ Product I 2019-09-04 12:35:00
zRnp0tLZRjSsQHN0wV7N_w Product J 2019-09-04 12:34:28
6Y3E3KkITYWbOs5aOQCHOw Product K 2019-09-04 10:33:38
ZOoG06ThRTiDDhteIW_6tA Product L 2019-09-04 10:19:14
6UW4MUClSLSuQI3pkA0qJA Product M 2019-09-04 10:18:40
Assume my application shows pages of 5 products at a time, ordered from newest to oldest.
I'm therefore ordering by creation date descending, and also ordering by ID so as to disambiguate between products that may have been created at the same moment.
I'm trying to fetch the results what would be the second page. The code (with relevant runtime values substituted in) looks like such:
selectFromWhere // <-- assume this to be a SelectConditionStep built with various filter criteria
.orderBy(TBL_PRODUCT.CREATED_AT.desc(), TBL_PRODUCT.ID.asc())
.seek(2019-09-06T08:21:24Z, "aTcWWxdJSReZBGzkLXuNIQ") // <-- runtime values
.limit(limit)
.fetchInto(Product::class.java)
This generates the following SQL (fully-qualified references and filter criteria omitted for brevity):
select distinct
id, created_at
from tbl_product
where (
(
created_at < {ts '2019-09-06 08:21:24.0'}
or (
created_at = {ts '2019-09-06 08:21:24.0'}
and id > 'aTcWWxdJSReZBGzkLXuNIQ'
)
)
)
order by
created_at desc,
id asc
limit 5
If I copy/paste and run the generated query manually from a SQL session, I get the results I expect:
Product F
Product G
Product H
Product I
Product J
...however, the results of the execution are saved into a local variable, and when I debug my program to examine its contents, I see it contains:
Product I
Product J
Product K
Product L
Product M
Two questions:
Why would jOOQ return different results for the same query I run manually against the same database?
Is there something wrong with my approach?
Any suggestions would be greatly appreciated!
Assuming the database column type for TBL_PRODUCT.CREATED_AT is DATETIME and the corresponding Java type is java.sql.Timestamp (which would be the default in jOOQ 3.11), this situation could arise when the time zone of the MySQL server differs from that of the Java client, since the JDBC driver will convert the timestamp for you (see https://stackoverflow.com/a/14070771/1732086 for details).
This behavior can also be controlled using various JDBC connection URL parameters (see https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html). One option is to use the serverTimezone JDBC URL property to specify the client's time zone as the session time zone to be used (e.g. serverTimezone=Europe/Zurich).
Time zones can always cause nasty surprises, especially in the context of JDBC :-(

Joining and selecting multiple tables and creating new column names

I have very limited experience with MySQL past standard queries, but when it comes to joins and relations between multiple tables I have a bit of an issue.
I've been tasked with creating a job that will pull a few values from a mysql database every 15 minutes but the info it needs to display is pulled from multiple tables.
I have worked with it for a while to figure out the relationships between everything for the phone system and I have discovered how I need to pull everything out but I'm trying to find the right way to create the job to do the joins.
I'm thinking of creating a new table for the info I need, with columns named as:
Extension | Total Talk Time | Total Calls | Outbound Calls | Inbound Calls | Missed Calls
I know that I need to start with the extension ID from my 'user' table and match it with 'extensionID' in my 'callSession'. There may be multiple instances of each extensionID but each instance creates a new 'UniqueCallID'.
The 'UniqueCallID' field then matches to 'UniqueCallID' in my 'CallSum' table. At that point, I just need to be able to say "For each 'uniqueCallID' that is associated with the same 'extensionID', get the sum of all instances in each column or a count of those instances".
Here is an example of what I need it to do:
callSession Table
UniqueCallID | extensionID |
----------------------------
A 123
B 123
C 123
callSum table
UniqueCallID | Duration | Answered |
------------------------------------
A 10 1
B 5 1
C 15 0
newReport table
Extension | Total Talk Time | Total Calls | Missed Calls
--------------------------------------------------------
123 30 3 1
Hopefully that conveys my idea properly.
If I create a table to hold these values, I need to know how I would select, join and insert those things based on that diagram but I'm unable to construct the right query/statement.
You simply JOIN the two tables, and do a group by on the extensionID. Also, add formulas to summarize and gather the info.
SELECT
`extensionID` AS `Extension`,
SUM(`Duration`) AS `Total Talk Time`,
COUNT(DISTINCT `UniqueCallID`) as `Total Calls`,
SUM(IF(`Answered` = 1,0,1)) AS `Missed Calls`
FROM `callSession` a
JOIN `callSum` b
ON a.`UniqueCallID` = b.`UniqueCallID`
GROUP BY a.`extensionID`
ORDER BY a.`extensionID`
You can use a join and group by
select
a.extensionID
, sum(b.Duration) as Total_Talk_Time
, count(b.Answered) as Total_Calls
, count(b.Answered) -sum(b.Answered) as Missed_calls
from callSession as a
inner join callSum as b on a.UniqueCallID = b.UniqueCallID
group by a.extensionID
This should do the trick. What you are being asked to do is to aggregate the number of and duration of calls. Unless explicitly requested, you do not need to create a new table to do this. The right combination of JOINs and AGGREGATEs will get the information you need. This should be pretty straightforward... the only semi-interesting part is calculating the number of missed calls, which is accomplished here using a "CASE" statement as a conditional check on whether each call was answered or not.
Pardon my syntax... My experience is with SQL Server.
SELECT CS.Extension, SUM(CA.Duration) [Total Talk Time], COUNT(CS.UniqueCallID) [Total Calls], SUM(CASE CS.Answered WHEN '0' THEN SELECT 1 ELSE SELECT 0 END CASE) [Missed Calls]
FROM callSession CS
INNER JOIN callSum CA ON CA.UniqueCallID = CS.UniqueCallID
GROUP BY CS.Extension

MySQL joining with PHP

Sometimes my eyes goes bonkers with these joins. Please help me build the select statement
product_version
id version
----------------
1 apple
2 orange
3 pineapple
executions
id class methods plat_version orig_prod_version
-----------------------------------------------------------------
1 SomeTest check 2 1
2 AnotTest submit 3 2
I want to pull from the executions but convert the numbers from the version that is in the other table. I was trying to start off and just do one at this point. Here is what I have
SELECT e.id,
e.class,
e.plat_version,
pv.id,
pv.version,
pv.version AS plat_version FROM executions e JOIN product_versions pv ON pv.version = e.plat_version
Thanks for the help.
UPDATE:
I am hoping that it pulls the records from the executions table but instead of seeing numbers for plat_version and orig_prod_version, I want to see corresponding version fields from the other table
I'm thinking something like this:
SELECT
e.id,
e.class,
pv_plat.version AS plat_version,
pv_orig.version AS orig_prod_version,
FROM executions e
JOIN product_versions pv_plat ON pv_plat.id= e.plat_version
JOIN product_versions pv_orig ON pv_orig.id= e.orig_prod_version
The idea is that you just join to the product_versions table twice, once for each id column that you have in the executions table.
SELECT *
FROM executions e
LEFT JOIN product_version v
ON e.plat_version = v.id
LEFT JOIN product_version v2
ON e.orig_prod_version = v2.id
SQL JOIN Explanation

MySQL relational database query, correct terminology?

I think my issue with databases stems from not knowing the correct terminology to help me find an answer myself so I'll explain a generic version of what I'm doing and hopefully you can point some tutorials my way or give me some terms to check into.
Let's use an example of an employee directory.
Each employee can have multiple locations, multiple job duties which pull from a separate table. Example tables & some data, let's just focus on the multiple locations.
employees
Main employee data
- id (ex: 400)
- first (ex: John)
- last (ex: Doe)
locations
Unique list of locations
- id (ex: 3000)
- title (ex: FakeCo, LLC)
map_employees_locations
Tie ANY number of locations to an employee
- id
- employee_id (ex: 400)
- location_id (ex: 3000)
I'm struggling with the logic of how a single query would return something like this:
John
Doe
FakeCo, LLC
AnotherCo, LLC
It seems I would have to run a query to get the employee data, then within a nested query, grab locations associated with the employee id, etc... If there was only one location per employee, it would be a simple join, I just don't know how to handle the multiples.
Let me know if I'm way off, I'm just struggling.
You would join all of the tables together like this
select e.id,e.first,e.last,l.id,l.title
from employees e
inner join map_employees_locations el
on el.employee_id = e.id
inner join locations l
on el.location_id = l.id
where e.first = 'John'
AND e.last = 'Doe'
This would return data like this:
e.id e.first e.last l.id l.title
------------------------------------------------
1 John Doe 1 FakeCo, LLC
1 John Doe 2 AnotherCo, LLC
If you want only one line per employee you should maybe use group concat
select id, e.last, e.first
group_concat(l.title separator ',' ) as locations
from employee e
join location l on l.employee_id = e.id
group by e.id
Not sure about the syntax cos i'm more aware of postgres but this should do the job.

Finding values from a table that are *not* in a grouping of another table and what group that value is missing from?

I hope I am not missing something very simple here. I have done a Google search(es) and searched through Stack Overflow.
Here is the situation: For simplicity's sake let's say I have a table called "PeoplesDocs", in a SQL Server 2008 DB, that holds a bunch of people and all the documents that they own. So one person can have several documents. I also have a table called "RequiredDocs" that simply holds all the documents that a person should have. Here is sort of what it looks like:
PeoplesDocs:
PersonID DocID
-------- -----
1 A
1 B
1 C
1 D
2 C
2 D
3 A
3 B
3 C
RequiredDocs:
DocID DocName
----- ---------
A DocumentA
B DocumentB
C DocumentC
D DocumentD
How do I write a SQL query that returns some variation of:
PersonID MissingDocs
-------- -----------
2 DocumentA
2 DocumentB
3 DocumentD
I have tried, and most of my searching has pointed to, something like:
SELECT DocID
FROM DocsRequired
WHERE NOT EXIST IN (
SELECT DocID FROM PeoplesDocs)
but obviously this will not return anything in this example because everyone has at least one of the documents.
Also, if a person does not have any documents then there will be one record in the PeoplesDocs table with the DocID set to NULL.
How about something like this:
Select ...
From RequiredDocs As RD
Cross Join People As P
Where Not Exists(
Select 1
From PeoplesDocs As PD1
Where PD1.PersonId = P.PersonId
And PD1.DocId = RD.DocId
)
SELECT
p.PersonID,
rd.DocName AS MissingDocs
FROM
dbo.People p, dbo.RequiredDocs rd
WHERE
rd.DocID NOT IN (SELECT pd.DocID FROM dbo.PeoplesDocs pd
WHERE pd.PersonID = p.PersonID)