Can nested query in JPQL access outer query - sql

So I'm curious if a nested SELECT can reference it's outer SELECT in order to compare values. I haven't been able to test or see many examples on this topic.
As an example, I'm trying to write a query to select all Clothes rows that has a tag (some number) that is within a given list and has the highest time that is prior to given time (which is total number of seconds). The query in question is below:
SELECT c FROM Clothes c WHERE c.tag IN :tagList
AND (c.timeOfSale = (SELECT MAX(n.timeOfSale) FROM Clothes k
WHERE (c.tag = k.tag) AND (k.timeOfSale) < (:time))) GROUP BY c.tag
Is the comparison c.tag = k.tag valid? If not, is there an alternative?

#Query("SELECT b FROM Business b WHERE b <> :currentBusiness "
+ "and exists "
+ "(Select i from InterestMaster i, BusinessInterest bI where bI.interestMaster = i and bI.business = b"
+ "and i in (:userInterests))")
Page<Business> getCommunityBusiness(#Param("currentBusiness") Business currentBusiness, #Param("userInterests") List<InterestMaster> userInterests,Pageable pageable);
I am using the above JPQL and its working fine. So yes nested query can access outer query.

Yes. They're called correlated queries, where the subquery is evaluated for each row of outer query.

Related

Sub-query works but would a join or other alternative be better?

I am trying to select rows from one table where the id referenced in those rows matches the unique id from another table that relates to it like so:
SELECT *
FROM booklet_tickets
WHERE bookletId = (SELECT id
FROM booklets
WHERE bookletNum = 2000
AND seasonId = 9
AND bookletTypeId = 3)
With the bookletNum/seasonId/bookletTypeId being filled in by a user form and inserted into the query.
This works and returns what I want but seems messy. Is a join better to use in this type of scenario?
If there is even a possibility for your subquery to return multiple value you should use in instead:
SELECT *
FROM booklet_tickets
WHERE bookletId in (SELECT id
FROM booklets
WHERE bookletNum = 2000
AND seasonId = 9
AND bookletTypeId = 3)
But I would prefer exists over in :
SELECT *
FROM booklet_tickets bt
WHERE EXISTS (SELECT 1
FROM booklets b
WHERE bookletNum = 2000
AND seasonId = 9
AND bookletTypeId = 3
AND b.id = bt.bookletId)
It is not possible to give a "Yes it's better" or "no it's not" answer for this type of scenario.
My personal rule of thumb if number of rows in a table is less than 1 million, I do not care optimising "SELECT WHERE IN" types of queries as SQL Server Query Optimizer is smart enough to pick an appropriate plan for the query.
In reality however you often need more values from a joined table in the final resultset so a JOIN with a filter WHERE clause might make more sense, such as:
SELECT BT.*, B.SeasonId
FROM booklet_tickes BT
INNER JOIN booklets B ON BT.bookletId = B.id
WHERE B.bookletNum = 2000
AND B.seasonId = 9
AND B.bookletTypeId = 3
To me it comes down to a question of style rather than anything else, write your code so that it'll be easier for you to understand it months later. So pick a certain style and then stick to it :)
The question however is old as the time itself :)
SQL JOIN vs IN performance?

SQL query to filter by specific date criteria

SQL query to filter by specific date criteria
SQL Server Management Studio V17.7
Question: I am looking for guidance related to a view on how to select records where the Start_Date falls between a defined date range when either the Admit_Status = 1 or Admit_Status = 0 as shown below:
Criteria should be something like: if admit status = 1 then dbo.PT_ASSIGNMENT.START_DATE >= referral_date to ifnull dbo.PT_ADMISSION.TERMINATION_DATE then now() else dbo.PT_ADMISSION.TERMINATION_DATE or if admit status = 0 then start_date >= referral_date to ifnull dbo.PT_ADMISSION.PROSPECT_TERM_DATE then now() else dbo.PT_ADMISSION.PROSPECT_TERM_DATE
My SQL query (View) excluding the above question:
SELECT dbo.RES_BASIC.RESOURCE_ID,
dbo.PT_ADMISSION.ADMISSION_ID,
dbo.PT_ASSIGNMENT.START_DATE,
(CASE PT_ADMISSION.PROSPECT_ADMIT_DATE WHEN NULL THEN PT_ADMISSION.ADMIT_DATE ELSE PT_ADMISSION.PROSPECT_ADMIT_DATE END) AS REFERRAL_DATE,
dbo.PT_ADMISSION.ADMIT_DATE,
dbo.PT_ADMISSION.PROSPECT_ADMIT_DATE,
dbo.PT_ADMISSION.PROSPECT_TERM_DATE,
dbo.PT_ADMISSION.TERMINATION_DATE,
CASE WHEN PT_ADMISSION.ADMIT_DATE IS NOT NULL THEN 1 ELSE 0 END AS ADMIT_STATUS,
dbo.PT_BASIC.PATIENT_CODE,
dbo.RES_BASIC.NAME_FULL
FROM dbo.PT_BASIC
INNER JOIN dbo.PT_STATUS
ON dbo.PT_BASIC.PATIENT_ID = dbo.PT_STATUS.PATIENT_ID
INNER JOIN dbo.A_PATIENT_STATUS
ON dbo.PT_STATUS.ADMIN_SET_ID = dbo.A_PATIENT_STATUS.ADMIN_SET_ID
AND dbo.PT_STATUS.STATUS_CODE = dbo.A_PATIENT_STATUS.STATUS_CODE
INNER JOIN dbo.O_DATASET
ON dbo.PT_BASIC.DATASET_ID = dbo.O_DATASET.DATASET_ID
INNER JOIN dbo.PT_ADMISSION
ON dbo.PT_BASIC.PATIENT_ID = dbo.PT_ADMISSION.PATIENT_ID
AND dbo.PT_STATUS.ADMISSION_ID = dbo.PT_ADMISSION.ADMISSION_ID
INNER JOIN dbo.PT_ASSIGNMENT
ON dbo.PT_BASIC.PATIENT_ID = dbo.PT_ASSIGNMENT.PATIENT_ID
INNER JOIN dbo.A_ASSIGNMENT_TYPE
ON dbo.PT_ASSIGNMENT.ADMIN_SET_ID = dbo.A_ASSIGNMENT_TYPE.ADMIN_SET_ID
AND dbo.PT_ASSIGNMENT.ASSIGNMENT_TYPE = dbo.A_ASSIGNMENT_TYPE.TYPE_ID
INNER JOIN dbo.RES_BASIC
ON dbo.PT_ASSIGNMENT.RESOURCE_ID = dbo.RES_BASIC.RESOURCE_ID
WHERE (dbo.O_DATASET.DATASET_NAME = 'XXXXXXXXXX')
AND (dbo.A_ASSIGNMENT_TYPE.DESCRIPTION = 'REFERRING PHYSICIAN')
GROUP BY dbo.RES_BASIC.NAME_FIRST + ' ' + dbo.RES_BASIC.NAME_LAST,
dbo.RES_BASIC.RESOURCE_ID,
dbo.PT_ADMISSION.ADMISSION_ID,
dbo.PT_BASIC.PATIENT_CODE,
dbo.PT_ASSIGNMENT.START_DATE,
dbo.PT_ADMISSION.PROSPECT_TERM_DATE,
dbo.PT_ADMISSION.PROSPECT_ADMIT_DATE,
dbo.PT_ADMISSION.TERMINATION_DATE,
dbo.PT_ADMISSION.ADMIT_DATE,
dbo.RES_BASIC.NAME_FULL
You can put pretty much any "if" into a condition with simple AND and OR use.
I am having a hard time mentally parsing your "something like" portion, but to give a generic example.
IF A THEN B ELSE C
can be translated to (A AND B) OR (NOT A AND C)
Note: Bill Braskey's "comment" is also worth considering. If the conditional logic gets complicated enough, it can be less work for the database to UNION queries with simpler conditions. You'd still need the condition in one to be A AND B and the other to be NOT A AND C to apply the conditions appropriately, but you'd be simplifying from the overall condition (especially when you consider "C" could actually be a translation of IF D THEN E ELSE F.
Maybe just take the easy way out and write two queries with the separate requirements and do a union

MS Access SQL: Enumerate results from a many-to-one relationship

Very simply, I have a many-to-one relationship table set in MS Access where I've managed to pull out the distinct values as separate rows. I now need to enumerate these rows.
The query looks like the following (generated by the MS Access Designer - apologies for the formatting):
SELECT DISTINCT ValidationRule.ValidationCode AS Rule, Table.Template AS Template
FROM ValidationRule RIGHT JOIN (([Table] INNER JOIN TableVersion ON Table.TableID = TableVersion.TableID) INNER JOIN ValidationScope ON TableVersion.TableVID = ValidationScope.TableVID) ON ValidationRule.ValidationId = ValidationScope.ValidationID
GROUP BY ValidationRule.ValidationCode, Table.Template
ORDER BY ValidationRule.ValidationCode;
So my data looks like:
Rule Template
v0007_m C 00.01
v0189_h C 01.00
v0189_h C 05.01
v3000_i C 08.00
I need to add sequential values to the results as follows:
Rule Template Sequence
v0007_m C 00.01 1
v0189_h C 01.00 1
v0189_h C 05.01 2
v3000_i C 08.00 1
What function should I be looking at in MS Access SQL to do this?
If you save the query you have as a separate query called qryValdationRule, this query which builds off that should give you what you need:
SELECT qryValidationRule.Rule, qryValidationRule.Template, DCount("*", 'qryValidationRule', "[Rule] = '" & qryValidationRule.Rule & "' AND [Template] <= '" & qryValidationRule.Template & "'") AS Sequence
FROM qryValidationRule
ORDER BY qryValidationRule.Rule, qryValidationRule.Template;
We are looking up and getting a count of all records with the same Rule value with an equal or less Template value within the dataset. This, essentially, gives us a Sequence grouped by Rule. This only works properly if Template values are distinct across Rule groups, which should be the case because you are pulling a DISTINCT across the CROSS JOIN of tables. It is not as convenient or flexible as window functions, but will get you what you need.
You may also want to try this method, which may be more efficient:
SELECT t1.Rule, t1.Template, COUNT(t2.Template) AS Sequence
FROM qryValidationRule AS t1 INNER JOIN qryValidationRule AS t2 ON t1.Rule = t2.Rule AND t1.Template >= t2.Template
GROUP BY t1.Rule, t1.Template
ORDER BY t1.Rule, t1.Template;
EDIT: Added an alternative way to find the same data; may be more performant because of JOINing vs. subqueries.
Use: Count(*) AS Sequence
SELECT DISTINCT ValidationRule.ValidationCode AS Rule, Table.Template AS Template, Count(*) AS Sequence
FROM ValidationRule RIGHT JOIN (([Table] INNER JOIN TableVersion ON Table.TableID = TableVersion.TableID) INNER JOIN ValidationScope ON TableVersion.TableVID = ValidationScope.TableVID) ON ValidationRule.ValidationId = ValidationScope.ValidationID
GROUP BY ValidationRule.ValidationCode, Table.Template
ORDER BY ValidationRule.ValidationCode;

LINQ to SQL: Left join a table with itself and compute an average

I'm trying to write the following query in LINQ to SQL. The table contains a list of sessions arranged by users, and the query computes the average amount of time between consecutive sessions for each user. It uses a left join, so that users who have only one session have a NULL value.
SELECT t1.workerId, AVG(DATEDIFF(s, t1.endTime, t2.startTime))
FROM e_userLongSessions t1
LEFT JOIN e_userLongSessions t2
ON t1.workerId = t2.workerId AND t1.sessionNum = t2.sessionNum - 1
GROUP BY t1.workerId
ORDER BY t1.workerId
Based on the questions LINQ to SQL Left Outer Join and How to do joins in LINQ on multiple fields in single join, I've gotten to the following query:
from s1 in gzClasses.e_userLongSessions
join s2 in gzClasses.e_userLongSessions
on new {w = s1.workerId, n = s1.sessionNum} equals new {w = s2.workerId, n = s2.sessionNum - 1}
into joined
from s2 in joined.DefaultIfEmpty(null)
group new {s1, s2} by s1.workerId into g
select g.Average(e => e.s2 == null ? (double?) null : (e.s2.startTime - e.s1.endTime).TotalSeconds);
I'm getting a Unsupported overload used for query operator 'DefaultIfEmpty' message. Any suggestions?
Your LINQ query is not structured the same way the T-SQL query is. Specifically, you are only including s in the grouping. In fact, s is named in a misleading way. It should be s2. Include both:
from s1 in gzClasses.e_userLongSessions
join s2 in gzClasses.e_userLongSessions
on new {w = s1.workerId, n = s1.sessionNum}
equals new {w = s2.workerId, n = s2.sessionNum - 1}
into joined
from s2Null in joined.DefaultIfEmpty()
group new {s1, s2Null} by s1.workerId into g
orderby g.Key // workerId
select g.Average(e => (e.s2Null.startTime - e.s1.endTime).TotalSeconds);
Now, you have data from both tables available in the aggregate. I don't think both of your queries are taking into account that s2.startTime can be null. But that is a bug that is not the point of the question.

Access substitute for EXCEPT clause

How can I get the same result I would get with the SQL code below in ms access? It does not recognize the EXCEPT clause...
SELECT DISTINCT
P.Name,
T.Training
FROM Prof AS P,
Training_done AS TC,
Trainings AS T
WHERE (P.Name Like '*' & NameProf & '*')
AND (P.Primary_Area = T.Cod_Area)
EXCEPT
SELECT DISTINCT
P.Name,
T.Training
FROM Prof AS P,
Training_done AS TC,
Trainings AS T
WHERE (P.Name Like '*' & NameProf & '*')
AND (P.Cod_Prof = TC.Cod_Prof);
Thanks in advance!
In order to get rid of the EXCEPT you could combine the conditions and negate the second one:
SELECT DISTINCT
P.Name,
T.Training
FROM Prof AS P,
Training_done AS TC,
Trainings AS T
WHERE ((P.Name Like '*' & NameProf & '*') AND
(P.Primary_Area = T.Cod_Area))
AND NOT ((P.Name Like '*' & NameProf & '*') AND
(P.Cod_Prof = TC.Cod_Prof));
SELECT A.x FROM A
EXCEPT
SELECT B.x FROM B
corresponds to
SELECT A.x FROM A
LEFT JOIN B
ON A.x = B.x
WHERE B.x IS NULL
use the find unmatched wizard in MS Access > Create > Query Wizard and you will get the following result
Union is a separate Access Query which i used to union a few tables instead of using sub queries
SELECT TableMain.Field1
FROM TableMain LEFT JOIN [Union] ON TableMain.[Field1] = Union.[field1]
WHERE (((Union.field1) Is Null));
Not an explicit example here, but consider UNION-ing the two fetched tables and selecting, from that union, pairs that have fewer than 2 instances of a certain field combination. This implies that, where each table has more than one instance of a record with the same values on the field combination, these records are the same and can be eliminated from result set. Where not, they are unique to one table, leaving fetch with only records from the selected table where there is no match to the other table. Kind of like a poor-man's "EXCEPT" KW.