realization of an algorithm in SQL query - sql

I have a database which has 2 tables:
CREATE TABLE RecipeDB (
RecipeID INT PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR,
Recipe VARCHAR,
Origin VARCHAR,
Category VARCHAR,
Favoured BOOL);
CREATE TABLE IngredientDB (
RecipeID REFERENCES RecipeDB.RecipeID,
Ingredient VARCHAR,
Quantity VARCHAR);
(One-to-many relation between Recipe and Ingredients)
I also have an actionscript, in which I have ingArr:Array of ingredient strings.
Now, I would like to realize the following queries here:
1) Select (all fields) one recipe which has the most of ingredients from the array. If more than one record have the same amount of matches, then divide the number of matches by total number of ingredients in recipe and return the one with the highest ratio. If there are no matches return nothing.
2) As above, but return 10 recipes with the most matches and do not perform check for equal number of matches. Sort the results by the number of matches.
Any ideas how to compose those queries in SQLite?

(The SQL statement provide under are for SQLite)
So for the second one you need the top 10 recipee that match the most ingredient
What you need is:
count the row that match your ingredient list (use IN operator)
order the result by best count in descendant order (4,3,2,...)
limit the result by 10
So the sql statement looks like
SELECT
r.RecipeId, COUNT(1) cnt, r.Name, r.Recipe, r.Origin, r.Category, r.Favoured
FROM
RecipeDB r
INNER JOIN IngredientDB i USING(RecipeID)
WHERE
i.Ingredient in ('ingr_1',..,'ingr_x')
GROUP BY 1
ORDER BY 2 DESC
LIMIT 10
using AIR + AS3 it can be something like that :
var sqls:SQLStatement = new SQLStatement()
sqls.sqlConnection = YOUR SQL CONNECTION
// your ingredient list
var ingredients:Array = ['i2', 'i3', 'i4']
// use to build the in parameter array
var inParams:Array = []
// fill parameter values
for(var i:int = 0; i < ingredients.length; ++i) {
inParams[i] = '?'
sqls.parameters[i] = ingredients[i]
}
// build the query
var qry:String = "SELECT r.RecipeId, COUNT(1) cnt, r.Name, r.Recipe, r.Origin,"+
"r.Category, r.Favoured FROM RecipeDB r INNER JOIN IngredientDB i USING(RecipeID)"+
"WHERE i.Ingredient in (" + inParams.join(',') + ") GROUP BY 1 "+
"ORDER BY 2 DESC LIMIT 10"
// set the query
sqls.text = qry
//execute
sqls.execute()
And for the first one same idea as above but you need to count also all the ingredient present into the recipee to provide a ratio between match / total
What you need is:
count the row that match your ingredient list (use IN operator)
make a rank by divide previous count by all total ingredient
get the best match
limit the result by 1
So the sql statement looks like :
SELECT
i1.RecipeId, (cast(rs.cnt as real) / cast (COUNT(1) as real)) rank,
rs.Name, rs.Recipe, rs.Origin, rs.Category, rs.Favoured
FROM
IngredientDB i1
INNER JOIN (
SELECT
r.RecipeId, COUNT(1) cnt, r.Name, r.Recipe, r.Origin, r.Category, r.Favoured
FROM
RecipeDB r
INNER JOIN IngredientDB i USING(RecipeID)
WHERE
i.Ingredient in ('ingr_1',..,'ingr_x')
GROUP BY 1
) rs USING (RecipeId)
GROUP BY 1
ORDER BY 2 DESC
LIMIT 1
Using the same logic as for the first example your query can be written as :
var ingredients:Array = ['i2', 'i3', 'i4']
var inParams:Array = []
for(var i:int = 0; i < ingredients.length; ++i) {
inParams[i] = '?'
sqls.parameters[i] = ingredients[i]
}
var qry:String = "SELECT i1.RecipeId, (cast(rs.cnt as real) / cast (COUNT(1) as real)) rank,"+
"rs.Name, rs.Recipe, rs.Origin, rs.Category, rs.Favoured "+
"FROM IngredientDB i1 INNER JOIN ("+
"SELECT r.RecipeId, COUNT(1) cnt, r.Name, r.Recipe, r.Origin, r.Category, r.Favoured "+
"FROM RecipeDB r INNER JOIN IngredientDB i USING(RecipeID) "+
"WHERE i.Ingredient in (" + inParams.join(',') + ") GROUP BY 1) rs USING (RecipeId) "+
"GROUP BY 1 ORDER BY 2 DESC LIMIT 1"

The queries can be tuned slightly, but the T-SQL below demonstrates the answers your looking for in a fairly readable way.
BEGIN
-- setup test
DECLARE #one TABLE(id INT, name VARCHAR(10))
DECLARE #many TABLE(pid INT, name VARCHAR(10))
INSERT INTO #one VALUES
(1, 'AAA'),
(2, 'BBB'),
(3, 'CCC')
INSERT INTO #many VALUES
(1, 'x'),(1, 'y'),(1, 'z'),
(2, 'x'),(2, 'y'),
(3, 'z')
--
-- WHERE m.name IN ('x', 'y')
-- 'x', 'y' represent your list of ingrediants
-- answer 1
SELECT * FROM #one WHERE id = (
SELECT TOP 1 x.id FROM (
SELECT o.id, COUNT(o.id) 'match', (SELECT COUNT(*) FROM #many WHERE pid=o.id) 'total' FROM #one o
INNER JOIN #many m ON o.id = m.pid
WHERE m.name IN ('x', 'y', 'z')
GROUP BY o.id
) as x ORDER BY x.match DESC, x.match/x.total DESC
)
-- answer 2
SELECT * FROM #one WHERE id IN (
SELECT TOP 10 x.id FROM (
SELECT o.id, COUNT(o.id) 'match' FROM #one o
INNER JOIN #many m ON o.id = m.pid
WHERE m.name IN ('x', 'y')
GROUP BY o.id
) as x ORDER BY x.match DESC
)
END

Related

SQL Server add a score if an answer is the same as the correct answer with group by clause

I have this code and its temporary tables so you can run it.
create table #student(
id int identity(1,1),
name varchar(50)
)
create table #quiz(
id int identity(1,1),
name varchar(50),
points_worth int
)
create table #exam(
id int identity(1,1),
sequence int,
question varchar(50),
answer varchar(50),
quiz_id int
)
create table #student_taken(
id int identity(1,1),
sequence int,
answer varchar(50),
student_id int,
quiz_id int
)
insert into #student(name)
values('Uzumaki Naruto'),('Uchiha Sasuke'),('Haruno Sakura')
insert into #quiz(name,points_worth)
values('Chunin Exam',2)
insert into #exam(sequence,question,answer,quiz_id)
values(1,'Hinata and Neji are siblings','True',1),
(2,'Uchiha Sasuke is part of the Akatsuki','False',1),
(3,'Tsunade and Jiraiya has a son','False',1)
insert into #student_taken(sequence,answer,quiz_id,student_id)
values(1,'True',1,1),(2,'True',1,1),(3,'True',1,1),(1,'True',1,2),(2,'False',1,2),(3,'False',1,2),
(1,'True',1,3),(2,'False',1,3),(3,'False',1,3)
drop table #student
drop table #exam
drop table #quiz
drop table #student_taken
So as you can see I Uzumaki Naruto only has 1 point, cause he only got 1 correct answer, and both Sakura and Sasuke has 3 points each.
Now I want it to be like this:
id name score
1 Uzumaki Naruto 2
2 Uchiha Sasuke 6
3 Haruno Sakura 6
He got 6 because in my #quiz table i added points worth(it is points worth for each number of items).
So now I was wondering if group by clause is needed to this? and what is the correct summation, I want that if True = True then it adds 1 point and False = False same and False = True will not count.
Here is my attempt
select
ST.student_id,
SUM(1 * Q.points_worth) 'sum'
from #student_taken ST
inner join #exam E
on E.quiz_id = ST.quiz_id
inner join #quiz Q
on Q.id = E.quiz_id
group by ST.student_id
I'm not really sure what your question is here. #JorgeCampos isn't quite correct, in that a GROUP BY is only required if you are returning aggragated and non-aggrated fields in the same dataset (without the use of a OVER clause).
As for getting the result set, I'm not quite sure how you came to the conclusion you did. The value of points_worth is in your quiz table, not the exam table, so I assume every question has the same value for that quiz? If so, this is one idea for your query:
SELECT q.[name] AS QuizName,
s.[name] As StudentName,
COUNT(CASE WHEN st.answer = e.answer THEN 1 END) * q.points_worth AS Score,
COUNT(CASE WHEN st.answer = e.answer THEN 1 END) AS Correct,
COUNT(CASE WHEN st.answer != e.answer THEN 1 END) AS Incorrect
FROM #student s
JOIN #student_taken st ON s.id = st.student_id
JOIN #exam e ON st.[sequence] = e.id
JOIN #quiz q ON e.quiz_id = q.id
GROUP BY q.[name], s.[name],
q.points_worth;
We could however, go a little further and see if a Student actually answered all the questions (and exclude those that answered none), thus we get:
INSERT INTO #quiz([name],points_worth)
VALUES('Basic Maths',1);
INSERT INTO #exam([sequence],question,answer,quiz_id)
VALUES(1,'5 + 2 * 3 = 21','False',2),
(2,'9 - 1 * 2 = 7','True',2);
INSERT INTO #student ([name])
VALUES ('Jane Smith'),('Sally Bloggs');
INSERT INTO #student_taken ([sequence],answer,quiz_id,student_id)
VALUES (1, 'false', 1, 4),
(1, 'false', 2, 4),
(2, 'true', 2, 4),
(1, 'true', 2, 5);
GO
SELECT q.[name] AS QuizName,
s.[name] As StudentName,
COUNT(CASE WHEN st.answer = e.answer THEN 1 END) * q.points_worth AS Score,
COUNT(CASE WHEN st.answer = e.answer THEN 1 END) AS Correct,
COUNT(CASE WHEN st.answer != e.answer THEN 1 END) AS Incorrect,
COUNT(CASE WHEN st.answer IS NULL THEN 1 END) AS Unanswered
FROM #quiz q
JOIN #exam e ON q.id = e.quiz_id
CROSS JOIN #student s
LEFT JOIN #student_taken st ON s.id = st.student_id
AND e.[sequence] = st.[sequence]
AND q.id = st.quiz_id
WHERE EXISTS (SELECT 1 FROM #student_taken sq WHERE sq.student_id = s.id AND sq.quiz_id = q.id)
GROUP BY q.[name], s.[name],
q.points_worth;
Hope that helps.
You can try this method: find how many correct points / student (query inside of CTE) and then take result and join the #quiz table to calculate the final points by applying the points_worth
;with cte as (
select
st.student_id
,st.quiz_id
,COUNT(e.id) as points
from #student_taken st
left join #exam e
on st.quiz_id = e.quiz_id
and st.answer = e.answer
and st.sequence = e.sequence
group by st.student_id, st.quiz_id
) select
student_id
,s.name
--,quiz_id
,points * q.points_worth
from cte
inner join #quiz q
on quiz_id = q.id
inner join #student s
on student_id = s.id
Add a conditional inside the sum. I also noticed the join to from #student_taken to #exam wasn't working correctly because you're only joining on quiz_id, while you also need to join on sequence.
So here's your attempt with those modifications:
select
ST.student_id,
SUM(IIF(ST.answer = E.answer, Q.points_worth, 0)) 'sum'
from #student_taken ST
inner join #exam E
on E.quiz_id = ST.quiz_id and ST.sequence = E.sequence
inner join #quiz Q
on Q.id = st.quiz_id
group by ST.student_id
The IIF function evaluates the first parameter as a conditional, returns the second parameter if true, and the third parameter if false. So if the student answered what the exam expects (ST.answer = E.answer), it returns the points worth, otherwise 0.
If you'd rather not use IIF, you can use a case statement: case when ST.answer = E.answer then Q.points_worth else 0 end. I personally just think IIF looks cleaner, and SQL Server Management Studio goes a bit crazy with syntax hinting if you mess up a case statement.

conditional where clause

I need filter result of a join query between two tables but I don't have a condition for the "where clause".
What I need is to filter based on id_project like this:
if id_project is equal to 24 (24 is the default project) then it should return only rows with
id_project =24. here rows 1,3...10 will be selected
if id_project is equal to 25, then I need those rows which have id_project=25 plus those rows which has " id_project=24 and not id_project 25, so rows number 2 to 11 will be selected
With this query :
SELECT tp.id_tag, tp.id_project, tp.NJTagName, tp.node_level , tl.id_level
FROM instrumentation.dbo.tag_project tp
INNER JOIN instrumentation.dbo.tag_level tl
ON tl.id_tag=tp.id_tag
//
where tl.id_level=69 and tp.node_level=1
I get this result :
How can I change my query to do this?
Thinking a little more about your request, it burns down to: For every id_tag give me the ID requested, or 24 if not available. This can be done in one query, where you use a ranking with ROW_NUMBER, in which you prefer the requested ID over the 24.
select *
from
(
select
tp.*,
row_number() over(partition by id_tag
order by case when id_project = 24 then 2 else 1 end) as rn
from tag_project tp
where id_project in (24, #REQUESTED_ID)
) ranked
where rn = 1;
Here is your original query changed accordingly:
SELECT id_tag, id_project, NJTagName, node_level, id_level
FROM
(
SELECT tp.id_tag, tp.id_project, tp.NJTagName, tp.node_level , tl.id_level
, row_number() over (partition by tp.id_tag order by case when tp.id_project = 24 then 2 else 1 end) as rn
FROM instrumentation.dbo.tag_project tp
INNER JOIN instrumentation.dbo.tag_level tl ON tl.id_tag=tp.id_tag
WHERE tl.id_level=69 AND tp.node_level=1
AND tp.id_project in (24, #REQUESTED_ID)
) ranked
WHERE rn = 1;
So depending of whether the user asks for ID 24 or not there are two different queries to execute.
Here is the query for ID 24:
select *
from tag_project
where id_project = 24;
Here is the query for #OTHERID. We use UNION ALL to combine the 24 records with the #OTHER records. And we use NOT EXISTS in order to avoid certain records.
select *
from tag_project
where id_project = #OTHERID
union all
select *
from tag_project tp
where id_project = 24
and not exists
(
select *
from tag_project tp2
where tp2.id_tag = tp.id_tag
and tp2.id_project = #OTHERID
);
You can write like this for default case pass #otherId as NULL.
SELECT tp.id_tag, tp.id_project, tp.NJTagName, tp.node_level , tl.id_level
FROM instrumentation.dbo.tag_project tp
INNER JOIN instrumentation.dbo.tag_level tl
ON tl.id_tag=tp.id_tag
where tl.id_level=69 and tp.node_level=1 AND tp.id_project = ISnull(#otherId, 24)
Use in:
SELECT tp.id_tag, tp.id_project, tp.NJTagName, tp.node_level , tl.id_level
FROM instrumentation.dbo.tag_project tp
INNER JOIN instrumentation.dbo.tag_level tl
ON tl.id_tag=tp.id_tag
//
where tl.id_level=69 and tp.node_level=1
and tp.id_project in (24, #OTHERID)
UPDATE:
If you want only one id_project instead, use can also use aggregation method.
SELECT tp.id_tag, tp.NJTagName, tp.node_level , tl.id_level
case when count(distinct tp.id_project) = 2 then #OTHERID
else 24 end as id_project
FROM instrumentation.dbo.tag_project tp
INNER JOIN instrumentation.dbo.tag_level tl
ON tl.id_tag=tp.id_tag
WHERE tl.id_level=69 and tp.node_level=1
AND tp.id_project in (24, #OTHERID)
GROUP BY tp.id_tag, tp.NJTagName, tp.node_level , tl.id_level

Replace no result

I have a query like this:
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
order by V.IDTipoVersamento,month(DataAllibramento)
This query must always return something. If no result is produced a
0 0 0 0
row must be returned. How can I do this. Use a isnull for every selected field isn't usefull.
Use a derived table with one row and do a outer apply to your other table / query.
Here is a sample with a table variable #T in place of your real table.
declare #T table
(
ID int,
Grp int
)
select isnull(Q.MaxID, 0) as MaxID,
isnull(Q.C, 0) as C
from (select 1) as T(X)
outer apply (
-- Your query goes here
select max(ID) as MaxID,
count(*) as C
from #T
group by Grp
) as Q
order by Q.C -- order by goes to the outer query
That will make sure you have always at least one row in the output.
Something like this using your query.
select isnull(Q.TipoVers, '0') as TipoVers,
isnull(Q.ImpTot, 0) as ImpTot,
isnull(Q.N, 0) as N,
isnull(Q.Mese, 0) as Mese
from (select 1) as T(X)
outer apply (
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese,
V.IDTipoVersamento
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
) as Q
order by Q.IDTipoVersamento, Q.Mese
Use COALESCE. It returns the first non-null value. E.g.
SELECT COALESCE(TV.Desc, 0)...
Will return 0 if TV.DESC is NULL.
You can try:
with dat as (select TV.[Desc] as TipyDesc, sum(Import) as ToImp, count(*) as N, month(Date) as Mounth
from /*DATA SOURCE HERE*/ as TV
group by [Desc], month(Date))
select [TipyDesc], ToImp, N, Mounth from dat
union all
select '0', 0, 0, 0 where (select count (*) from dat)=0
That should do what you want...
If it's ok to include the "0 0 0 0" row in a result set that has data, you can use a union:
SELECT TV.Desc as TipyDesc,
sum(Import) as TotImp,
count(*) as N,
month(Date) as Mounth
...
UNION
SELECT
0,0,0,0
Depending on the database, you may need a FROM for the second SELECT. In Oracle, this would be "FROM DUAL". For MySQL, no FROM is necessary

MySQL/SQL - When are the results of a sub-query avaliable?

Suppose I have this query
SELECT * FROM (
SELECT * FROM table_a
WHERE id > 10 )
AS a_results LEFT JOIN
(SELECT * from table_b
WHERE id IN
(SElECT id FROM a_results)
ON (a_results.id = b_results.id)
I would get the error "a_results is not a table". Anywhere I could use the re-use the results of the subquery?
Edit: It has been noted that this query doesn't make sense...it doesn't, yes. This is just to illustrate the question which I am asking; the 'real' query actually looks something like this:
SELECT SQL_CALC_FOUND_ROWS * FROM
( SELECT wp_pod_tbl_hotel . *
FROM wp_pod_tbl_hotel, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =12
AND wp_pod_rel.tbl_row_id =1
AND wp_pod.tbl_row_id = wp_pod_tbl_hotel.id
AND wp_pod_rel.pod_id = wp_pod.id
) as
found_hotel LEFT JOIN (
SELECT COUNT(*) as review_count, avg( (
location_rating + staff_performance_rating + condition_rating + room_comfort_rating + food_rating + value_rating
) /6 ) AS average_score, hotelid
FROM (
SELECT r. * , wp_pod_rel.tbl_row_id AS hotelid
FROM wp_pod_tbl_review r, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =11
AND wp_pod_rel.pod_id = wp_pod.id
AND r.id = wp_pod.tbl_row_id
AND wp_pod_rel.tbl_row_id
IN (
SELECT wp_pod_tbl_hotel .id
FROM wp_pod_tbl_hotel, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =12
AND wp_pod_rel.tbl_row_id =1
AND wp_pod.tbl_row_id = wp_pod_tbl_hotel.id
AND wp_pod_rel.pod_id = wp_pod.id
)
) AS hotel_reviews
GROUP BY hotel_reviews.hotelid
ORDER BY average_score DESC
AS sorted_hotel ON (id = sorted_hotel.hotelid)
As you can see, the sub-query which makes up the found_query table is repeated elsewhere downward as another sub-query, so I was hoping to re-use the results
You can not use a sub-query like this.
I'm not sure I understand your query, but wouldn't that be sufficient?
SELECT * FROM table_a a
LEFT JOIN table_b b ON ( b.id = a.id )
WHERE a.id > 10
It would return all rows from table_a where id > 10 and LEFT JOIN rows from table_b where id matches.

Multiple MAX values select using inner join

I have query that work for me only when values in the StakeValue don't repeat.
Basically, I need to select maximum values from SI_STAKES table with their relations from two other tables grouped by internal type.
SELECT a.StakeValue, b.[StakeName], c.[ProviderName]
FROM SI_STAKES AS a
INNER JOIN SI_STAKESTYPES AS b ON a.[StakeTypeID] = b.[ID]
INNER JOIN SI_PROVIDERS AS c ON a.[ProviderID] = c.[ID] WHERE a.[EventID]=6
AND a.[StakeGroupTypeID]=1
AND a.StakeValue IN
(SELECT MAX(d.StakeValue) FROM SI_STAKES AS d
WHERE d.[EventID]=a.[EventID] AND d.[StakeGroupTypeID]=a.[StakeGroupTypeID]
GROUP BY d.[StakeTypeID])
ORDER BY b.[StakeName], a.[StakeValue] DESC
Results for example must be:
[ID] [MaxValue] [StakeTypeID] [ProviderName]
1 1,5 6 provider1
2 3,75 7 provider2
3 7,6 8 provider3
Thank you for your help
There are two problems to solve here.
1) Finding the max values per type. This will get the Max value per StakeType and make sure that we do the exercise only for the wanted events and group type.
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
2) Then we need to get only one return back for that value since it may be present more then once.
Using the Max Value, we must find a unique row for each I usually do this by getting the Max ID is has the added advantage of getting me the most recent entry.
SELECT MAX(SMaxID.ID) AS ID
FROM SI_STAKES AS SMaxID
INNER JOIN (
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
) AS SMaxVal ON SMaxID.StakeTypeID = SMaxVal.StakeTypeID
AND SMaxID.StakeValue = SMaxVal.MaxStakeValue
AND SMaxID.EventID = SMaxVal.EventID
AND SMaxID.StakeGroupTypeID = SMaxVal.StakeGroupTypeID
3) Now that we have the ID's of the rows that we want, we can just get that information.
SELECT Stakes.ID, Stakes.StakeValue, SType.StakeName, SProv.ProviderName
FROM SI_STAKES AS Stakes
INNER JOIN SI_STAKESTYPES AS SType ON Stake.[StakeTypeID] = SType.[ID]
INNER JOIN SI_PROVIDERS AS SProv ON Stake.[ProviderID] = SProv.[ID]
WHERE Stake.ID IN (
SELECT MAX(SMaxID.ID) AS ID
FROM SI_STAKES AS SMaxID
INNER JOIN (
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
) AS SMaxVal ON SMaxID.StakeTypeID = SMaxVal.StakeTypeID
AND SMaxID.StakeValue = SMaxVal.MaxStakeValue
AND SMaxID.EventID = SMaxVal.EventID
AND SMaxID.StakeGroupTypeID = SMaxVal.StakeGroupTypeID
)
You can use the over clause since you're using T-SQL (hopefully 2005+):
select distinct
a.stakevalue,
max(a.stakevalue) over (partition by a.staketypeid) as maxvalue,
b.staketypeid,
c.providername
from
si_stakes a
inner join si_stakestypes b on
a.staketypeid = b.id
inner join si_providers c on
a.providerid = c.id
where
a.eventid = 6
and a.stakegrouptypeid = 1
Essentially, this will find the max a.stakevalue for each a.staketypeid. Using a distinct will return one and only one row. Now, if you wanted to include the min a.id along with it, you could use row_number to accomplish this:
select
s.id,
s.maxvalue,
s.staketypeid,
s.providername
from (
select
row_number() over (order by a.stakevalue desc
partition by a.staketypeid) as rownum,
a.id,
a.stakevalue as maxvalue,
b.staketypeid,
c.providername
from
si_stakes a
inner join si_stakestypes b on
a.staketypeid = b.id
inner join si_providers c on
a.providerid = c.id
where
a.eventid = 6
and a.stakegrouptypeid = 1
) s
where
s.rownum = 1