how to create this query - sql

how to create a query if i need to include two aggregate function in select row and per each function i need different group by and where conditions
in my example i need to returns the playerName, and how many the player win the this can be checked if the results in table game result= first, and how many times he played
but do not know how to deal with two aggregate functions .
simply i want to join the result of this two queries
1.
select playeName,count(*)
from player,game
where player.playerId=game.playerId and result="first"
group by game.playerId
2.
select count(*)
from game, player
where game.playerId=player.playerId
group by game.playerId
the set of attributes for table game are
playerId , result
the set of attributes for table player are
playerName,playerId
any idea???

Use:
SELECT p.playername,
SUM(CASE WHEN g.result = 'first' THEN 1 ELSE 0 END),
COUNT(*)
FROM PLAYER p
JOIN GAME g ON g.playerid = p.playerid
GROUP BY p.playername

Along with solutions proposed by OMG Ponies and Bnjmn, you can also get desired results by using WITH ROLLUP
select result, count(*)
from game, player
where game.playerId=player.playerId
group by game.playerId, result WITH ROLLUP
Then, on client side, find records with result equals 'first' and and result is null(which is #games played).

Related

Aggregating based on GROUPING of multiple columns

I am trying to subquery and aggregate in SQL after doing an initial query with multiple joins. My ultimate goal is to get a count (or a sum) of specimens tested based on a grouping of multiple columns. This is slightly different from SQL Server query - Selecting COUNT(*) with DISTINCT and SQL Server: aggregate error on grouping.
The three tables that I use (PERSON, SPECIMEN, TEST), have 1-many relationships. So PERSON has many SPECIMENS and those SPECIMENS have many TESTS. I did three inner joins to combine these tables plus an additional table (ANALYSIS).
WITH TALLY as (
SELECT PERSON.NAME, PERSON.PHASE, TEST.DATE_STARTED, TEST.ANALYSIS, SPECIMEN.GROUP, TEST.STATUS,
ANALYSIS.ANALYSIS_TYPE, SPECIMEN.SPECIMEN_NUMBER
FROM DB.TEST
INNER JOIN
DB.SAMPLE ON
TEST.SPECIMEN_NUMBER = SPECIMEN.SPECIMEN_NUMBER
INNER JOIN
DB.PRODUCT ON
SPECIMEN.PERSON = PERSON.NAME
INNER JOIN
DB.ANALYSIS ON
TEST.ANALYSIS = ANALYSIS.NAME
WHERE PERSON.NAME = 'Joe'
AND TEST.DATE_STARTED >= '20-DEC-16' AND TEST.DATE_STARTED <='01-APR-18'
AND PERSON.PHASE = 'PHASE1'
ORDER BY TEST.DATE_STARTED)
SELECT COUNT(DISTINCT ANALYSIS) as SPECIMEN_COUNT, DATE_STARTED, ANALYSIS, STATUS, GROUP, ANALYSIS_TYPE
FROM TALLY
GROUP BY DATE_STARTED, ANALYSIS, STATUS, GROUP, ANALYSIS_TYPE
ORDER BY DATE_STARTED;
This gives me the repeated columns: first grouping repeated 4 times
What I am trying to see is: aggregated first grouping with total count
Any thoughts as to what is missing? SUM instead of COUNT or in addition to COUNT creates an error. Thanks in advance!
9/17/2020 Update: I have tried adding a subquery because I also need to use a new column of metadata (ANALYSIS_TYPE_ALIAS) which is created in the first query through a CASE STATEMENT(...). I have also tried using another subquery with inner join to count based on those conditions to a temp table, but still cannot seem to aggregate to flatten the table. Here is my current attempt:
WITH TALLY as (
SELECT PERSON.NAME, PERSON.PHASE, TEST.DATE_STARTED, TEST.ANALYSIS, SPECIMEN.GROUP, TEST.STATUS,
ANALYSIS.ANALYSIS_TYPE...
FROM DB.TEST
INNER JOIN
DB.SAMPLE ON
TEST.SPECIMEN_NUMBER = SPECIMEN.SPECIMEN_NUMBER
INNER JOIN
DB.PRODUCT ON
SPECIMEN.PERSON = PERSON.NAME
INNER JOIN
DB.ANALYSIS ON
TEST.ANALYSIS = ANALYSIS.NAME
WHERE PERSON.NAME = 'Joe'
AND TEST.DATE_STARTED >= '20-DEC-16' AND TEST.DATE_STARTED <='01-APR-18'
AND PERSON.PHASE = 'PHASE1'
ORDER BY TEST.DATE_STARTED),
SUMMARY_COMBO AS (SELECT DISTINCT(CONCAT(CONCAT(CONCAT(CONCAT(ANALYSIS, DATE_STARTED),STATUS), GROUP), ANALYSIS_TYPE_ALIAS))AS UUID,
TALLY.NAME, TALLY.PHASE, TALLY.DATE_STARTED, TALLY.ANALYSIS, TALLY.GROUP, TALLY.STATUS, TALLY.ANALYSIS_TYPE_ALIAS
FROM TALLY)
SELECT SUMMARY_COMBO.NAME, SUMMARY_COMBO.PHASE, SUMMARY_COMBO.DATE_STARTED, SUMMARY_COMBO.ANALYSIS,SUMMARY_COMBO.GROUP, SUMMARY_COMBO.STATUS, SUMMARY_COMBO.ANALYSIS_TYPE_ALIAS,
COUNT(SUMMARY_COMBO.ANALYSIS) OVER (PARTITION BY SUMMARY_COMBO.UUID) AS SPECIMEN_COUNT
FROM SUMMARY_COMBO
ORDER BY SUMMARY_COMBO.DATE_STARTED;
This gave me the following table Shows aggregated counts, but doesn't aggregate based on unique UUID. Is there a way to take the sum of the count? I've tried to do this by storing count to a subquery and then referencing that count variable, but I am missing something in how to group the 8 columns of data that I want to show + the count of that combination of columns.
Thanks!
Just remove analysis from the group by clause, since that's the column whose distinct values you want to count. Otherwise, the query generates more groups than what you need (and the count of distinct analysis values in each group is always 1).
WITH TALLY as ( ...)
SELECT COUNT(DISTINCT ANALYSIS) as SPECIMEN_COUNT, DATE_STARTED, ANALYSIS, STATUS, GROUP, ANALYSIS_TYPE
FROM TALLY
GROUP BY DATE_STARTED, STATUS, GROUP, ANALYSIS_TYPE
ORDER BY DATE_STARTED;

Merging two query results in a materialized view

Im trying to merge two SELECT results into one view.
The first query returns the id's of all registered users.
The second query goes through an entire table and counts how many victories a player has and returns the id of the player and number of wins.
What I'm trying to do now is to merge these two results, so that if the user has wins it states how many but if he doesn't then it says 0.
I tried doing it like this:
SELECT profile.user_id
FROM profile
FULL JOIN ( SELECT player_game_data.user_id,
count(player_game_data.user_id) AS wins
FROM player_game_data
WHERE player_game_data.is_winner = 1
GROUP BY player_game_data.user_id) t2 ON profile.user_id::text = t2.user_id::text;
But in the end it only returns id's of the players and there isn't a count column:
What am I doing wrong?
Is this what you want?
select p.*,
(select count(*)
from player_game_data pg
where pg.user_id = p.user_id and pg.is_winner = 1
) as num_wins
from profile p;
Or, if all users have played at least one game, you can use conditional aggregation:
select pg.user_id,
count(*) filter (where pg.is_winner = 1)
from player_game_data pg
group by pg.user_id;
Or, if is_winner only takes on the values of 0 and 1:
select pg.user_id, sum(ps.is_winner)
from player_game_data pg
group by pg.user_id;
Thanks for the help Gordon. I've got it to work now.
The final query looks like this :
SELECT p.user_id,
( SELECT count(*) AS count
FROM player_game_data pg
WHERE pg.user_id::text = p.user_id::text AND pg.is_winner = 1) AS wins,
( SELECT count(*) AS count
FROM player_game_data pg
WHERE pg.user_id::text = p.user_id::text AND pg.is_winner = 0) AS losses,
( SELECT count(*) AS count
FROM player_game_data pg
WHERE pg.user_id::text = p.user_id::text) AS games_played
FROM profile p;
And when I run it I get the result that i wanted:

Select Query for Repeated Records in SQLite

This problem is a generalization of this question. Rather than finding all the games with specific players playing against others, I want to be able to find all the games where the same players played against each other.
Here is sample data:
1,ChrisEveret,1
1,BillieJeanKing,1
1,RogerFederer,0
1,TomasMuster,0
2,RogerFederer,1
2,SallieMae,1
2,NovakDjokovic,0
2,JimCourier,0
3,ChrisEveret,0
3,BillieJeanKing,0
3,RogerFederer,1
3,TomasMuster,1
The desired output is
1,ChrisEveret,1
1,BillieJeanKing,1
1,RogerFederer,0
1,TomasMuster,0
3,ChrisEveret,0
3,BillieJeanKing,0
3,RogerFederer,1
3,TomasMuster,1
The actual data has only about two thousand rows, so performance is not a concern. I have come up with the following remarkably convoluted and inexact partial solution:
CREATE TABLE sets (gameid int, player text ,winloss int);
.import data.csv sets
select * from sets where gameid in
(select gameid from (select gameid,mo from
(select gameid,mo,count(*) from
(select gameid,group_concat(player) as mo from
(select gameid,player from sets order by gameid,player)
group by gameid)
group by gameid)
where mo in
(select mo from (select gameid,mo,count(*) from
(select gameid,group_concat(player) as mo from
(select gameid,player from sets order by gameid,player)
group by gameid)
group by mo
having count(*)>1))));
This returns all matches where the same four people played together, but not necessarily those in which the teams were the same. I do not know if there is a solution to this problem that does not involve using group_concat(). That is the only way I was able to make even this limited progress on it, however. I also am not sure that the method used to order the group_concat results for aggregation will always work.
SQLite does not guarantee the ordering using group_concat() -- and there is no way to control it. So you have to use more cumbersome methods.
You can get the pairs of games with the same player using:
with s as (
select s.*, count(*) over (partition by gameid) as num_players
from sets s
)
select s1.gameid, s2.gameid
from s s1 join
s s2
on s1.player = s2.player and s1.num_players = s2.num_players
group by s1.gameid = s2.gameid
having count(*) = max(s1.num_players);
You can then use this logic if you want to get the players in each game (or just use group_concat() for that).
EDIT:
Window functions were introduced in SQLite version 3.28. In earlier versions, try this:
with s as (
select s.*, ss.num_players
from sets s join
(select gameid, count(*) as num_players
from sets s
group by gameid
) ss
on ss.gameid = s.gameid
)
select s1.gameid, s2.gameid
from s s1 join
s s2
on s1.player = s2.player and s1.num_players = s2.num_players
group by s1.gameid = s2.gameid
having count(*) = max(s1.num_players);
Here is a db<>fiddle that shows all pairs of games that have the same players (note that this includes each team to itself).

Is there a way to use DISTINCT and COUNT(*) together to bulletproof your code against DUPLICATE entries?

I got help with a function yesterday to correctly get the count of multiple items in a column based on multiple criteria/columns. However, if there is a way to get the DISTINCT count of all the entries in the table based on aggregated GROUP BY statement.
SELECT TIME = ap.day,
acms.tenantId,
acms.CallingService,
policyList = ltrim(sp.value),
policyInstanceList = ltrim(dp.value),
COUNT(*) AS DISTINCTCount
FROM dbo.acms_data acms
CROSS APPLY string_split(acms.policyList, ',') sp
CROSS APPLY string_split(acms.policyInstanceList, ',') dp
CROSS APPLY (select day = convert(date, acms.[Time])) ap
GROUP BY ap.day, acms.tenantId, sp.value, dp.value, acms.CallingService
I would just like to know if there would be a way to see if there is a workaround for using DISTINCT and Count(*) together and whether or not it would affect my results to make this algorithm potentially invulnerable to duplicate entries.
The reason why I have to use COUNT(*) is because I am aggregating based on every column in the table not just a specific column or multiple.
We can use DISTINCT with COUNT together like this example.
USE AdventureWorks2012
GO
-- This query shows 290 JobTitle
SELECT COUNT(JobTitle) Total_JobTitle
FROM [HumanResources].[Employee]
GO
-- This query shows only 67 JobTitle
SELECT COUNT( DISTINCT JobTitle) Total_Distinct_JobTitle
FROM [HumanResources].[Employee]
GO

calculate rank in highscore from 2 tables

i have a trivia game and i want to reward users for 2 events:
1) answering correctly
2) sending a question to the questions pool
i want to query for score and rank of a specific player and i use this query:
SELECT (correct*10+sent*30) AS score, #rank:=#rank+1 AS rank
FROM ( trivia_players
JOIN ( SELECT COUNT(*) AS sent, senderid
FROM trivia_questions
WHERE senderid='$userid'
) a
ON trivia_players.userid=a.senderid
)
ORDER BY score DESC
and it works if the player is in both tables i.e answered correctly AND sent a question.
but it doesn't work if a player hasn't sent a question
any idea how to fix this query? ($userid is the given parameter)
thanks!
Thanks Tom! only problem is the ranks are not correct:
userid score rank
58217 380 1
12354 80 3
32324 0 2
I would probably do it like this:
SELECT
user_id,
score,
rank
FROM
(
SELECT
TP.user_id,
(TP.correct * 10) + (COUNT(TQ.sender_id) * 30) AS score,
#rank:=#rank + 1 AS rank
FROM
Trivia_Players TP
LEFT OUTER JOIN Trivia_Questions TQ ON
TQ.sender_id = TP.user_id
GROUP BY
TP.user_id,
TP.correct
ORDER BY
score DESC
) AS SQ
WHERE
SQ.user_id = $user_id
I don't use MySQL much, so the syntax may not be perfect. I think that you can use a subquery like this in MySQL. Assuming that MySQL handles COUNT() by only counting rows with a non-null value for , this should work.
The keys are that you do a COUNT over a non-null column from Trivia Questions so that it counts them up by the user and you need to use a subquery so that you can get ranks for everyone BEFORE constraining to a particular user id.
Have you tried using a RIGHT JOIN or LEFT JOIN? Just off the top of my head!