SELECT *
FROM myTable m
WHERE m.userId = :userId
AND m.X = (SELECT MAX(X)
FROM myTable m
WHERE m.userId = :userId
AND m.contactNumber = :contactNumber)";
The problem is,second part of statement evaluates to null in case no such row is present and the statement fails to execute.I want the result to be empty in such a case.
One way to solve this problem is to do expensive filesort(order by) and then fetch the required field at code level. Any better solution to this problem ?
Can you use ISNULL?
and m.X = ISNULL(, '')
I'm not sure why you're getting NULLs here, but try this:
SELECT myTable.*, IF myTableMax.myMaxX IS NOT NULL myTableMax.myMaxX ELSE ""
FROM myTable
LEFT OUTER JOIN
(SELECT userID, contactNumber, MAX(X) AS myMaxX
FROM myTable
GROUP BY userID, contactNumber) AS myTableMax
ON myTable.userID = myTableMax.userID
AND myTable.contactNumber = myTableMax.contactNumber
WHERE myTable.userID = :userID
AND myTable.contactNumber = :contactNumber
If you're concerned about performance, add an index on mytable (userID, contactNumber).
Related
This is my query:
SELECT
`users`.`id` AS userID,
`user_tests`.`id`,
`users`.`profilePic`,
`users`.`firstName`,
`user_tests`.`userId`,
`user_tests`.`isFirstAttempt`,
`user_tests`.`total_marks`,
FIND_IN_SET(
`user_tests`.`total_marks`,
(
SELECT
GROUP_CONCAT(
DISTINCT `user_tests`.`total_marks`
ORDER BY
CAST(
`user_tests`.`total_marks` AS DECIMAL(5, 3)
)
DESC
)
FROM
`user_tests`
WHERE
`user_tests`.`testSeriesId` = '856' AND `user_tests`.`isFirstAttempt` = '1'
)
) AS rank,
FROM
`user_tests`
LEFT JOIN `users` ON `users`.id = `user_tests`.`userId`
WHERE
`user_tests`.`isFirstAttempt` = '1' AND `user_tests`.`testSeriesId` = '856'
ORDER BY
CAST(
`user_tests`.`total_marks` AS DECIMAL(5, 3)
)
DESC
,
`submissionTimeInMinutes` ASC,
`rank` ASC;
Output:
this is the image
Expected:
Here is the expected output image
I am using MariaDB 5.5.68
I've tried using variables to increment but it's showing me the current row number instead of 1,2,3,4,5... numbers.
Can anybody help here?
Thanks.
You don't appear to need a left join as none of the user names are missing. A simple subquery will give you the the ranking by total_mark but I'm not sure whether you're counting users or something else. A CTE would let you avoid duplicating some of the logic and might possibly even be faster. I don't know if you have that option.
SELECT
u.id AS userID, t.id, u.profilePic, u.firstName,
t.userId, t.isFirstAttempt, t.total_marks,
(
SELECT count(distinct userId) from FROM user_tests t2
WHERE t.isFirstAttempt = 1 AND t.testSeriesId = 856
AND t.total_marks < t2.total_marks
) as num_tests
FROM user_tests t inner join users u ON u.id = t.userId
WHERE t.isFirstAttempt = 1 AND t.testSeriesId = 856
ORDER BY total_marks desc, submissionTimeInMinutes, num_tests desc
I have two tables with one to many relation, like this:
MY_TABLE(ID, VALUE)
MY_TABLE_ATTRIBUTE(ID, MY_TABLE_ID, NAME, VALUE)
I want to check if there is record in MY_TABLE with particular attributes.
This query will probably explain what I am doing:
select 1
from MY_TABLE mt
where exists (select 1
from MY_TABLE_ATTRIBUTE mta
where mta.my_table_id = mt.id
and (mta.name = 'attributeName1' and mta.value = 'attributeValue1'))
and exists (select 1
from MY_TABLE_ATTRIBUTE mta
where mta.my_table_id = mt.id
and (mta.name = 'attributeName2' and mta.value = 'attributeValue2'))
and exists ...
.
.
.
My question is if there is a better (faster) way to check that existence.
Thanks in advance
Your query is fine. For performance, you want the following index:
create index idx_my_table_attribute_3 on my_table_attribute(my_table_id, name, value);
In fact, with this index, your code is probably the fastest way to implement this logic in Oracle.
Try the following rewrites:
select * from MY_TABLE mt
where exists (select 1 from MY_TABLE_ATTRIBUTE mta
where mta.my_table_id = mt.id
and(
(mta.name = 'attributeName1' and mta.value = 'attributeValue1')
OR (mta.name = 'attributeName2' and mta.value = 'attributeValue2')
OR ....
)
or
select * from MY_TABLE mt
where mt.id in (select mta.my_table_id from MY_TABLE_ATTRIBUTE mta
where
(mta.name = 'attributeName1' and mta.value = 'attributeValue1')
OR (mta.name = 'attributeName2' and mta.value = 'attributeValue2')
OR ....
)
An index on (name,value) may help if the cardinality of these columns is very high.
I'm stuck when rewriting fairly simple pure sql statement to criteria queries.
Basically I can simplify my query to
select max(T.value, S.value) from T, S where T.id = S.id
I have tried
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery();
Root<T> fromT = cq.from(T.class);
Root<S> fromS = cq.from(S.class);
cq.where(cb.equal(fromT.get("id), fromS.get("id"));
cq.select(); // <---- what to put here.
getEntityManager().createQuery(cq).getResultList();
I found greatest method but it compares horizontally (values from column) and I would like to compare two values from the same row but different column and get one with biggest value.
Only solution which comes to me mind is to get the results and sort them 'manually' but this can influence performance.
You can try the below case statement.
cb.selectCase().
when(cb.greaterThanOrEqualTo(fromT.get("xValue"), fromS.get("yValue")), fromT.get("xValue")). //-- If T has max value
otherwise(fromS.get("yValue")); //-- Else, by default select value from S
Can you just use a case statement?
SELECT
CASE
WHEN T.value < S.value THEN S.value
ELSE T.value
END
FROM
T, S
WHERE
T.id = S.id
I would also suggest changing this to an inner join if at all possible:
SELECT
CASE
WHEN T.value < S.value THEN S.value
ELSE T.value
END
FROM
T INNER JOIN S
ON T.id = S.id
I would like to take a sample of an Oracle table, but not include entries from another table. I have a query that currently works, but I'm pretty sure it will blow-up when the sub-select gets more than 1000 records.
select user_key from users sample(5)
where active_flag = 'Y'
and user_key not in (
select user_key from user_validation where validation_state <> 'expired'
);
How could this be re-written without the not in. I thought of using minus, but then my sample size would keep going down as new entries were added to the user_validation table.
You can do this with a left outer join:
select *
from (select u.user_key,
count(*) over () as numrecs
from users u left outer join
user_validation uv
on u.user_key = uv.user_key and
uv.validation_state <> 'expired'
where u.active_flag = 'Y' and uv.user_key is null
) t
where rownum <= numrecs * 0.05
You are using the sample clause. It is not clear if you just want the non-matches in the 5% you choose or if you want 5% of the data that is non-matches. This is the latter.
EDIT: Added example based on author's comment:
select user_key from (
select u.user_key, row_number() over (order by dbms_random.value) as randval
from users u
left outer join user_validation uv
on u.user_key = uv.user_key
and uv.validation_state <> 'expired'
where u.active_flag = 'Y'
and uv.user_key is null
) myrandomjoin where randval <=100;
select us.user_key
from users us -- sample(5)
where us.active_flag = 'Y'
and NOT EXISTS (
SELECT *
from user_validation nx
where nx.user_key = us.user_key
AND nx.validation_state <> 'expired'
);
BTW: I commented-out the sample(5) because I don't know what it means. (I strongly believe that it is not relevant, though)
select u.user_key from users u, user_validation uv
where u.active_flag = 'Y'
and u.user_key=uv.user_key
uv.validation_state= 'expired';
This was a double negation query, x not in list of non expired ids, which is equivalent to x is in the list of expired IDs, which is what I did, in addition to changing the subquery to a join.
Does anyone know a way to simplify this WHERE expression?
WHERE (
(#UserSpecialtyID in
(
SELECT CharacteristicSpecialties_Id
FROM ModalityVariantSpecialty
WHERE ModalityVariants_Id = ModalityVariants.Id
)
)
OR
NOT EXISTS
(
SELECT CharacteristicSpecialties_Id
FROM ModalityVariantSpecialty
WHERE ModalityVariants_Id = ModalityVariants.Id
)
)
Something like this should probably work but Im not exactly clear on the relationships for your tables. I could probably give a better example if you could explain the relationships.
SELECT
*
FROM MadalityVariants mv
LEFT JOIN ModalityVariantSpecialty mvs on mvs.ModalityVariants_ID = mv.ID
WHERE
#UserSpecialtyID = mvs.CharacteristicSpecialties_ID
OR
mvs.CharacteristicSpecialties_ID is null
WHERE (
#UserSpecialtyID in
(
SELECT COALESCE(CharacteristicSpecialties_Id, A.A)
FROM (SELECT #UserSpecialtyID A) A LEFT JOIN ModalityVariantSpecialty
ON ModalityVariants_Id = ModalityVariants.Id
)
)
this works well if CharacteristicSpecialties_Id is a NON NULLABLE field.
I am assuming that this is a WHERE clause of a SELECT on the table ModalityVariants
Would this work (The SQL is not tested)?
SELECT *
FROM ModalityVariants
LEFT OUTER JOIN ModalityVariantSpeciality
ON ModalityVariants.Id = ModalityVariants_ID
WHERE CharacteristicSpecialities_Id = #UserSpecialityID or
CharacteristicSpecialities_Id is NULL
Here's my attempt:
WHERE #UserSpecialtyID = COALESCE
(
SELECT TOP 1 CharacteristicSpecialties_Id
FROM ModalityVariantSpecialty
WHERE ModalityVariants_Id = ModalityVariants.Id
ORDER BY
CASE WHEN CharacteristicSpecialties_Id = UserSpecialtyID THEN 1
ELSE 2 END ASC
), #UserSpecialtyID)
If both ModalityVariants_Id and UserSpecialtyID match, the subquery returns CharacteristicSpecialties_Id, and the where succeeds
If only ModalityVariants_Id matches, the subquery returns a different ID, and the where fails
If neither matches, the subquery returns NULL, the COALESCE returns #UserSpecialtyID, and the where succeeds
Probably clearest is a variety of John Hartsock's answer, with a subquery to ensure the left join doesn't add any rows.
select *
from ModalityVariants mv
left join
(
select distinct ModalityVariants_ID
, CharacteristicSpecialties_ID
from ModalityVariantSpecialty
) as mvs
on mvs.ModalityVariants_ID = mv.ID
where #UserSpecialtyID = mvs.CharacteristicSpecialties_ID
OR
mvs.CharacteristicSpecialties_ID is null
I'll vote for John's answer :)