Division 2 lists distinct on 1 column - sql

select * from ChallengeResults
where ResultPercentage =
(
select CorrectAnswers from ChallengeResults a
inner join Challenge b on b.ChallengeId = a.ChallengeId
and a.ChallengeType = '2'
)
/
(
select NumberOfQuestions
from ChallengeConfiguration a
inner join Challenge b on a.ChallengeConfigurationId = b.ChallengeConfigurationId
inner join ChallengeResults c on c.ChallengeId = b.ChallengeId
and c.ChallengeType = '2'
);
This is my query.
This is the error :
Msg 512, Level 16, State 1, Line 11
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I want to put the division of these two columns in a single column.
This is used to calculate the percentage of correct answers for a test.
I want it to return a value in the ResultPercentage column.

That error message is because your subqueries return multiple values but are used in a way such that the DB is expecting a single value.
It sounds like you want to return a column ResultPercentage based on a calculation. The correct place to do this is in the SELECT clause of your query, not the WHERE clause.
Without table structure and sample data it might not be possible to give you the correct answer, but you likely want something along the lines of:
select CorrectAnswers / NumberOfQuestions ResultPercentage, c.*
from ChallengeConfiguration a
inner join Challenge b on a.ChallengeConfigurationId = b.ChallengeConfigurationId
inner join ChallengeResults c on c.ChallengeId = b.ChallengeId
and c.ChallengeType = '2'
This is your second subquery on its own, but note the SELECT portion is altered. It will return a column named ResultPercentage that is obtained by dividing CorrectAnswers by NumberOfQuestions for each row.

Related

Update Rows Where the following conditions are true

I am attempting to overwrite a field value with the value from another table where a certain condition is true. I have mocked up my code below
Pseudocode:
Where an employee has the team Ops in old_data, get their new team from new_data and overwrite the team in old_data
My Code:
UPDATE old_data -- This has columns Employee, Latest_Team
SET
Latest_Team =
(select new_data.team
from new_data
left join old data
ON old_data.employee = new_data.employee
)
WHERE old_data.employee = 'Ops'
But this is returning the error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I'm not sure where i am going wrong exactly
If you are looking for the team "ops", then your query has several issues:
You are filtering on the employee, not the team.
You have a typo in the inner from clause.
You want a correlated subquery.
So, I think you want:
UPDATE old_data -- This has columns Employee, Latest_Team
SET Latest_Team = (select nd.team
from new_data nd
where old_data.employee = nd.employee
)
WHERE od.latest_team = 'Ops';
------^ I think this is the filter you describe
Make sure that this query doesnt return anyting and your code will work
select new_data.team, count(*)
from new_data
left join old data WHERE old_data.employee = 'Ops'
group by new_data.team
having count(*) > 1
If you have more than one team per employee with Old_data.employee = 'Ops', sql wont know which to choose for that employee for the update
You got a typo in your join - "old data" should be "old_data".
Maybe this can help you: update columns values with column of another table based on condition
In your example it would be:
UPDATE old_data SET
Latest_Team = (SELECT new_data.team FROM new_data WHERE old_data.employee = new_data.employee LIMIT 1)
WHERE old_data.employee = 'Ops';

Setting subquery to variable to be used within the IN operator

I would like to use a variable to represent a result set that will be used in the WHERE clause of a query.
SELECT *
FROM Table1
WHERE
Exam1_ID IN (SELECT Id FROM Exam)
OR Exam2_ID IN (SELECT Id FROM Exam)
OR Exam3_ID IN (SELECT Id FROM Exam)
OR Exam4_ID IN (SELECT Id FROM Exam)
I would like to use a variable in place of SELECT Id FROM Exam so I don't have to keep repeating the query. I tried declaring a variable but since the results of the subquery could contain multiple integers I am not sure what the declare the variable as. I went ahead and tried ...
DECLARE #SubQuery INT;
SET #SubQuery = (SELECT Id FROM Exam);
SELECT *
FROM Table1
WHERE
Exam1_ID IN (#SubQuery)
OR Exam2_ID IN (#SubQuery)
OR Exam3_ID IN (#SubQuery)
OR Exam4_ID IN (#SubQuery)
I received the following error ..
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
You'd probably find that this performs better using a WHERE EXISTS like this
SELECT *
FROM Table1 t1
WHERE EXISTS ( SELECT *
FROM Exam e
WHERE t1.Exam1_ID = e.Id
OR t1.Exam2_ID = e.Id
OR t1.Exam3_ID = e.Id
OR t1.Exam4_ID = e.Id)
You can write an exists like this.
SELECT *
FROM Table1 t1
WHERE EXISTS (
SELECT 1 FROM Exam e
WHERE e.Id in ( t1.Exam1_ID , t1.Exam2_ID , t1.Exam3_ID, t1.Exam4_ID )
)
Lets begin from error message
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
The reason is pretty simple, you declared your subquery as INT
So if you will change it to TABLE(Id INT) you will receive new error message.
You can use different ways to solve your problem.
1) Inner join (It can give you duplicates, so I dont know is it valid for you to use this method)
DECLARE #SubQuery TABLE (RecordId INT)
INSERT INTO
#SubQuery
SELECT
Id
FROM
Exam
SELECT *
FROM
Table1 t1
INNER JOIN #Subquery sq
ON sq.Id = t1.Exam1_ID
OR sq.Id = t1.Exam2_ID
OR sq.Id = t1.Exam3_ID
OR sq.Id = t1.Exam4_ID
2) Exist
Please find sample in Rich Benner answer. He posted it while I was typing. But I found one typo there, he used different aliases in exists block
OR t1.Exam2_ID = e.Id --t1 - correct alias
OR t2.Exam2_ID = e.Id --t2 - incorrect
OR t3.Exam2_ID = e.Id --t3 - incorrect
OR t4.Exam2_ID = e.Id --t4 - incorrect

SQL query: Iterate over values in table and use them in subquery

I have a simple SQL table containing some values, for example:
id | value (table 'values')
----------
0 | 4
1 | 7
2 | 9
I want to iterate over these values, and use them in a query like so:
SELECT value[0], x1
FROM (some subquery where value[0] is used)
UNION
SELECT value[1], x2
FROM (some subquery where value[1] is used)
...
etc
In order to get a result set like this:
4 | x1
7 | x2
9 | x3
It has to be in SQL as it will actually represent a database view. Of course the real query is a lot more complicated, but I tried to simplify the question while keeping the essence as much as possible.
I think I have to select from values and join the subquery, but as the value should be used in the subquery I'm lost on how to accomplish this.
Edit: I oversimplified my question; in reality I want to have 2 rows from the subquery and not only one.
Edit 2: As suggested I'm posting the real query. I simplified it a bit to make it clearer, but it's a working query and the problem is there. Note that I have hardcoded the value '2' in this query two times. I want to replace that with values from a different table, in the example table above I would want a result set of the combined results of this query with 4, 7 and 9 as values instead of the currently hardcoded 2.
SELECT x.fantasycoach_id, SUM(round_points)
FROM (
SELECT DISTINCT fc.id AS fantasycoach_id,
ffv.formation_id AS formation_id,
fpc.round_sequence AS round_sequence,
round_points,
fpc.fantasyplayer_id
FROM fantasyworld_FantasyCoach AS fc
LEFT JOIN fantasyworld_fantasyformation AS ff ON ff.id = (
SELECT MAX(fantasyworld_fantasyformationvalidity.formation_id)
FROM fantasyworld_fantasyformationvalidity
LEFT JOIN realworld_round AS _rr ON _rr.id = round_id
LEFT JOIN fantasyworld_fantasyformation AS _ff ON _ff.id = formation_id
WHERE is_valid = TRUE
AND _ff.coach_id = fc.id
AND _rr.sequence <= 2 /* HARDCODED USE OF VALUE */
)
LEFT JOIN fantasyworld_FantasyFormationPlayer AS ffp
ON ffp.formation_id = ff.id
LEFT JOIN dbcache_fantasyplayercache AS fpc
ON ffp.player_id = fpc.fantasyplayer_id
AND fpc.round_sequence = 2 /* HARDCODED USE OF VALUE */
LEFT JOIN fantasyworld_fantasyformationvalidity AS ffv
ON ffv.formation_id = ff.id
) x
GROUP BY fantasycoach_id
Edit 3: I'm using PostgreSQL.
SQL works with tables as a whole, which basically involves set operations. There is no explicit iteration, and generally no need for any. In particular, the most straightforward implementation of what you described would be this:
SELECT value, (some subquery where value is used) AS x
FROM values
Do note, however, that a correlated subquery such as that is very hard on query performance. Depending on the details of what you're trying to do, it may well be possible to structure it around a simple join, an uncorrelated subquery, or a similar, better-performing alternative.
Update:
In view of the update to the question indicating that the subquery is expected to yield multiple rows for each value in table values, contrary to the example results, it seems a better approach would be to just rewrite the subquery as the main query. If it does not already do so (and maybe even if it does) then it would join table values as another base table.
Update 2:
Given the real query now presented, this is how the values from table values could be incorporated into it:
SELECT x.fantasycoach_id, SUM(round_points) FROM
(
SELECT DISTINCT
fc.id AS fantasycoach_id,
ffv.formation_id AS formation_id,
fpc.round_sequence AS round_sequence,
round_points,
fpc.fantasyplayer_id
FROM fantasyworld_FantasyCoach AS fc
-- one row for each combination of coach and value:
CROSS JOIN values
LEFT JOIN fantasyworld_fantasyformation AS ff
ON ff.id = (
SELECT MAX(fantasyworld_fantasyformationvalidity.formation_id)
FROM fantasyworld_fantasyformationvalidity
LEFT JOIN realworld_round AS _rr
ON _rr.id = round_id
LEFT JOIN fantasyworld_fantasyformation AS _ff
ON _ff.id = formation_id
WHERE is_valid = TRUE
AND _ff.coach_id = fc.id
-- use the value obtained from values:
AND _rr.sequence <= values.value
)
LEFT JOIN fantasyworld_FantasyFormationPlayer AS ffp
ON ffp.formation_id = ff.id
LEFT JOIN dbcache_fantasyplayercache AS fpc
ON ffp.player_id = fpc.fantasyplayer_id
-- use the value obtained from values again:
AND fpc.round_sequence = values.value
LEFT JOIN fantasyworld_fantasyformationvalidity AS ffv
ON ffv.formation_id = ff.id
) x
GROUP BY fantasycoach_id
Note in particular the CROSS JOIN which forms the cross product of two tables; this is the same thing as an INNER JOIN without any join predicate, and it can be written that way if desired.
The overall query could be at least a bit simplified, but I do not do so because it is a working example rather than an actual production query, so it is unclear what other changes would translate to the actual application.
In the example I create two tables. See how outer table have an alias you use in the inner select?
SQL Fiddle Demo
SELECT T.[value], (SELECT [property] FROM Table2 P WHERE P.[value] = T.[value])
FROM Table1 T
This is a better way for performance
SELECT T.[value], P.[property]
FROM Table1 T
INNER JOIN Table2 p
on P.[value] = T.[value];
Table 2 can be a QUERY instead of a real table
Third Option
Using a cte to calculate your values and then join back to the main table. This way you have the subquery logic separated from your final query.
WITH cte AS (
SELECT
T.[value],
T.[value] * T.[value] as property
FROM Table1 T
)
SELECT T.[value], C.[property]
FROM Table1 T
INNER JOIN cte C
on T.[value] = C.[value];
It might be helpful to extract the computation to a function that is called in the SELECT clause and is executed for each row of the result set
Here's the documentation for CREATE FUNCTION for SQL Server. It's probably similar to whatever database system you're using, and if not you can easily Google for it.
Here's an example of creating a function and using it in a query:
CREATE FUNCTION DoComputation(#parameter1 int)
RETURNS int
AS
BEGIN
-- Do some calculations here and return the function result.
-- This example returns the value of #parameter1 squared.
-- You can add additional parameters to the function definition if needed
DECLARE #Result int
SET #Result = #parameter1 * #parameter1
RETURN #Result
END
Here is an example of using the example function above in a query.
SELECT v.value, DoComputation(v.value) as ComputedValue
FROM [Values] v
ORDER BY value

pass an outer selects row variable to inner select in oracle

How do you pass an outer selects row variable to inner select in oracle, here is a sample query ( other outer joins has been removed. This query will be loaded 1 time in life time of an application). This query works
select l5.HIERARCHY_ID,
(select wm_concat(isbn) isbns from (
select op.isbn from oproduct op
LEFT JOIN assignment ha on op.r.reference = ha.reference
where ha.hierarchy_id = '100589'))isbns
from level l5 where l5.gid = '1007500000078694'
but when I change the inner select's where clause
where ha.hierarchy_id = '100589'))isbns
to
where ha.hierarchy_id = l5.HIERARCHY_ID))isbns
I get the following error
ORA-00904: "L5"."HIERARCHY_ID": invalid identifier
You cannot pass the value of a 2nd level SELECT.
For example -
SELECT value1 -- 1st level select
FROM (
SELECT value2 -- 2nd level select
FROM (
SELECT value3 -- 3rd level select.
You can have values from the 1st level SELECT available for only the second level SELECT.
Similarly the values in the second level SELECT are only available to the 1st level SELECT and the 3rd level SELECT not beyond that.
I did something like this to fix the problem. There was one unnecessary select
select
l5.HIERARCHY_ID,
(
select
wm_concat(op.isbn)
from
oproduct op
LEFT JOIN assignment ha on op.r.reference = ha.reference
where ha.hierarchy_id = l5.HIERARCHY_ID
) ISBNS
from
level l5
where
l5.gid = '1007500000078694'
I think I am reading your SQL correctly - you want an outer join when the hierarchy ids match?
SELECT
l5.hierarchy_id,
op.isbns
FROM
LEVEL l5
LEFT OUTER JOIN
(SELECT
wm_concat (op.isbn) isbns,
ha.hierarch_id
FROM
oproduct op
LEFT JOIN
assignment ha
ON op.reference = ha.reference) op
ON l5.gid = op.hierarchy_id

SQL update records in one table that satisify a query result on a related table

I'm trying to update a number of records in one table that are based on the result of a query from a related table. In this example, I have 150 records in tbl_events that I want to update with "entered_by", and in order to get those 150 records, I need to match the selection with the 150 records in tbl_locations has the value "needs update" in the notes field. The "Entered_by" value DOES NOT exist in tbl_locations, I'm just trying to update the table based on relationship pre-conditions. But I get the following error:
UPDATE TBL_EVENTS
SET Entered_By = 'Fred'
FROM GRSTBL_EVENTS as sp
JOIN TBL_LOCATIONS as so
On sp.Location_ID = so.Location_ID
AND so.Notes =(SELECT Notes from TBL_LOCATIONS where Notes = 'needs update')
Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.
you can use in
UPDATE TBL_EVENTS
SET Entered_By = 'Fred'
FROM GRSTBL_EVENTS as sp
JOIN TBL_LOCATIONS as so
On sp.Location_ID = so.Location_ID
AND so.Notes in (SELECT Notes from TBL_LOCATIONS where Notes = 'needs update')
If I understand your data structures correctly then you need to update the entered_by field if there are any notes in the related tbl_locations table.
If so then the following, which is known as a correlated subquery should do what you want
UPDATE TBL_EVENTS
SET Entered_By = 'Fred'
WHERE EXISTS (
SELECT 1
FROM TBL_LOCATIONS
WHERE TBL_LOCATIONS.Location_ID = TBL_EVENTS.Location_ID
AND TBL_LOCATIONS.Notes = 'needs update'
)