sql - return one row from each group - sql

Considering the tables below, what is the best way to only return the team membership and its related player, but only one player from each team? The important part to note is that selection needs to be based on a given set of player ids. That is the starting point.
Using the example below I would have the player ids: 1,2,3 (among others) and what I need to end up with is the unique memberships from a list of user ids(1,2,3 in this case). I would want to end up with team_memberships with id 1 & 2
Thanks!
player
id | name
1 | bob
2 | joe
3 | tom
team_memberships
id | team_id | player_id
1 | 1 | 1
2 | 2 | 2
3 | 1 | 3
team
id | name
1 | jets
2 | kings

Do you mean
Select team_id, Min(player_id)
From team_Memberships
Where player_id In (1,2,3)
Group By team_id
If the player selected needs to be based on some other attribute in the players table, then:
Select team_id,
(Select Min(player_id) -- in case more than one player satisfies criterion
From players
where playerId = m.Player_Id
And [Some other ctiterion]) playerId
From team_Memberships m
Where player_id In (1,2,3)
Group By team_id

Related

Sum of a column value of table B in table A, is there a automated way ? Is it good practice ? - Oracle SQL

Basically each user has a team, and each team has 11 players, so whenever a player scores they earn some points. Now is there a automated way to do this -
As in when there is a update/entry in the USER_TEAM_PLAYERS table, summate the points of all players to the USER_TEAM table for the corresponding user in some column (in this case TEAM_TOTAL column).
I have two tables:
USER_TEAM with columns USER_ID, TEAM_TOTAL
USER_TEAM_PLAYERS with columns PLAYER_NAME, PLAYER_POINTS, USER_ID
Example:
TABLE - USER_TEAM
USER_ID | TEAM_TOTAL
---------------------
1 | 40
2 | 50
TABLE - USER_TEAM_PLAYERS
PLAYER_NAME | PLAYER_POINTS | USER_ID
-------------------------------------
Adam | 10 | 1
Alex | 30 | 1
Botas | 40 | 2
Pepe | 5 | 2
Diogo | 5 | 2
The first table should be only a view of the second one
CREATE VIEW USER_TEAM2 AS
SELECT USER_ID, SUM(PLAYER_POINTS) AS TEAM_TOTAL
FROM USER_TEAM_PLAYERS
GROUP BY USER_ID
ORDER BY USER_ID;
Doing this, you have no duplicate data and a view can be in SELECT, ... like a table.
Nota 1 : I used the name USER_TEAM2 because your first table still exists but you can delete it.
Nota 2 : If you want to have some specific data to the TEAM_TABLE, keep the 2 names, and modifify your view as needed by adding some fields with a JOIN of this first table.

SQL query doesn't retrieve correct result with count

I have these tables
Actor: id | name
Acting: actor_id| movie|id
Movie: id | title
I have the code which returns how many the number of movies that an actor has acted in
SELECT a.name AS name, COUNT(ag.actor_id)
FROM actor a
LEFT JOIN acting ag ON a.id = ag.actor_id
GROUP BY a.id, a.name
ORDER BY COUNT(*) ASC
LIMIT 10;
name | count
--------------------------+-------
Bianca Brigitte VanDamme | 1
Karin Konoval | 1
Keri Maletto | 1
Terence Bernie Hines | 1
Jean Stapleton | 1
Kyle Hebert | 1
Brandon Middleton | 1
Timothy Webber | 1
Dana Hanna | 1
Travis Betz | 1
After inserting a random actor to the actors table, I run the same code but the output does not have the new actor.
INSERT INTO Actor
VALUES (5000, 'Jeremy Bearimy')
-- Run same code
SELECT a.name ........
FROM ...
I get this result:
name | count
--------------------------+-------
Bianca Brigitte VanDamme | 1
Karin Konoval | 1
Keri Maletto | 1
Terence Bernie Hines | 1
Jean Stapleton | 1
Kyle Hebert | 1
Brandon Middleton | 1
Timothy Webber | 1
Dana Hanna | 1
Travis Betz | 1
When I run the query with the code to see which actor has acted in 0 movies, I get a result, so I don't know why they don't appear in the query result.
SELECT a.name AS name, COUNT(ag.actor_id)
FROM actor a
LEFT JOIN acting ag ON a.id = ag.actor_id
GROUP BY a.id, a.name
HAVING COUNT(ag.actor_id) = 0
ORDER BY COUNT(*) ASC
LIMIT 10;
Output:
name | count
----------------+-------
Jeremy Bearimy | 0
Note your order by clause in the first query:
ORDER BY COUNT(*) ASC
Actors that are in 1 movie will have a COUNT(*) of 1. I think that is obvious.
Actors that are in 0 movies will also have a COUNT(*) of 1. Why? Because there is one row in the group even if the columns from the second table are NULL.
You are then limiting to 10 results. There is no second ORDER BY key, so the 10 returned rows are an arbitrary mix, starting with the actors that are in 0 or 1 movies.
If you instead used:
ORDER BY COUNT(ag.actor_id) ASC
then the actors with zero movies would appear before those with 1 movie.
from the last output it looks like actor 'Jeremy Bearimy' is missing in the acting table
Your query returns the 10 first actor, and they all have 1 movie. You have at least 10 actors with 1 movie. The ones that don't have movies can't appear in the result.
Remove the limit 10 and you will see all the actors with a movie or not.

In a query (no editing of tables) how do I join data without any similarities?

I Have a query that finds a table, here's an example one.
Name |Age |Hair |Happy | Sad |
Jon | 15 | Black |NULL | NULL|
Kyle | 18 |Blonde |YES |NULL |
Brad | 17 | Blue |NULL |YES |
Name and age come from one table in a database, hair color comes from a second which is joined, and happy and sad come from a third table.My goal would be to make the first line of the chart like this:
Name |Age |Hair |Happy |Sad |
Jon | 15 |Black |Yes |Yes |
Basically I want to get rid of the rows under the first and get the non NULL data joined to the right. The problem is that there is no column where the Yes values are on the Jon row, so I have no idea how to get them there. Any suggestions?
PS. With the data I am using I can't just put a 'YES' in the 'Jon' row and call it a day, I would need to find the specific value from the lower rows and somehow get that value in the boxes that are NULL.
Do you just want COALESCE()?
COALESCE(Happy, 'Yes') as happy
COALESCE() replaces a NULL value with another value.
If you want to join on a NULL value work with nested selects. The inner select gets an Id for NULLs, the outer select joins
select COALESCE(x.Happy, yn_table.description) as happy, ...
from
(select
t1.Happy,
CASE WHEN t1.Happy is null THEN 1 END as happy_id
from t1 ...) x
left join yn_table
on x.xhappy_id = yn_table.id
If you apply an ORDER BY to the query, you can then select the first row relative to this order with WHERE rownum = 1. If you don't apply an ORDER BY, then the order is random.
After reading your new comment...
the sense is that in my real data the yes under the other names will be a number of a piece of equipment. I want the numbers of the equipment in one row instead of having like 8 rows with only 4 ' yes' values and the rest null.
... I come to the conclusion that this a XY problem.
You are asking about a detail you think will solve your problem, instead of explaining the problem and asking how to solve it.
If you want to store several pieces of equipment per person, you need three tables.
You need a Person table, an Article table and a junction table relating articles to persons to equip them. Let's call this table Equipment.
Person
------
PersonId (Primary Key)
Name
optional attributes like age, hair color
Article
-------
ArticleId (Primary Key)
Description
optional attributes like weight, color etc.
Equipment
---------
PersonId (Primary Key, Foreign Key to table Person)
ArticleId (Primary Key, Foreign Key to table Article)
Quantity (optional, if each person can have only one of each article, we don't need this)
Let's say we have
Person: PersonId | Name
1 | Jon
2 | Kyle
3 | Brad
Article: ArticleId | Description
1 | Hat
2 | Bottle
3 | Bag
4 | Camera
5 | Shoes
Equipment: PersonId | ArticleId | Quantity
1 | 1 | 1
1 | 4 | 1
1 | 5 | 1
2 | 3 | 2
2 | 4 | 1
Now Jon has a hat, a camera and shoes. Kyle has 2 bags and one camera. Brad has nothing.
You can query the persons and their equipment like this
SELECT
p.PersonId, p.Name, a.ArticleId, a.Description AS Equipment, e.Quantity
FROM
Person p
LEFT JOIN Equipment e
ON p.PersonId = e.PersonId
LEFT JOIN Article a
ON e.ArticleId = a.ArticleId
ORDER BY p.Name, a.Description
The result will be
PersonId | Name | ArticleId | Equipment | Quantity
---------+------+-----------+-----------+---------
3 | Brad | NULL | NULL | NULL
1 | Jon | 4 | Camera | 1
1 | Jon | 1 | Hat | 1
1 | Jon | 5 | Shoes | 1
2 | Kyle | 3 | Bag | 2
2 | Kyle | 4 | Camera | 1
See example: http://sqlfiddle.com/#!4/7e05d/2/0
Since you tagged the question with the oracle tag, you could just use NVL(), which allows you to specify a value that would replace a NULL value in the column you select from.
Assuming that you want the 1st row because it contains the smallest age:
- wrap your query inside a CTE
- in another CTE get the 1st row of the query
- in another CTE get the max values of Happy and Sad of your query (for your sample data they both are 'YES')
- cross join the last 2 CTEs.
with
cte as (
<your query here>
),
firstrow as (
select name, age, hair from cte
order by age
fetch first row only
),
maxs as (
select max(happy) happy, max(sad) sad
from cte
)
select f.*, m.*
from firstrow f cross join maxs m
You can try this:
SELECT A.Name,
A.Age,
B.Hair,
C.Happy,
C.Sad
FROM A
INNER JOIN B
ON A.Name = B.Name
INNER JOIN C
ON A.Name = B.Name
(Assuming that Name is the key columns in the 3 tables)

PostgreSQL - GROUP BY condition, if a value is in one of two columns

Let's say I have sports teams with games. The games table has the following fields:
home_id, away_id
Where home_id and away_id are foreign keys relating to teams.
I want to somehow do an operation (GROUP BY?) where I get all of the games in which a team's ID is either in the away_id column or home_id column for all of the teams.
Is there a way to do this?
Thanks in advance!
You don't need GROUP BY to achieve this. The query is pretty self explanatory:
SELECT * FROM games WHERE home_id = some_team_id OR away_id = some_team_id
If I understand OP right he wanted the matrix below. Which he could filter:
t=# with s(game,home,away) as (values(1,1,2),(2,2,1),(3,2,3),(4,3,2),(5,1,5))
, m as (select unnest(array[home,away]) ar,game from s)
select ar team,count(1),array_agg(game) games from m group by ar order by ar;
team | count | games
------+-------+-----------
1 | 3 | {2,5,1}
2 | 4 | {4,3,1,2}
3 | 2 | {3,4}
5 | 1 | {5}
(4 rows)

SQLite: How can I add data from from different rows when summing up other ones?

I have a card game analyzation program that stores the results in an SQLite database with integer columns for the following values: round, table, game, pair, and score.
The schema of the "score" table is:
CREATE TABLE score(
round INTEGER,
table_number INTEGER,
game INTEGER,
pair_or_player INTEGER,
score INTEGER
);
E. g. a simple case for only two pairs and one round with two games each, where 21 points can be reached looks like this:
+-------+--------------+------+----------------+-------+
| round | table_number | game | pair_or_player | score |
+-------+--------------+------+----------------+-------+
| 1 | 1 | 1 | 1 | 8 |
| 1 | 1 | 1 | 2 | 21 |
| 1 | 1 | 2 | 1 | 21 |
| 1 | 1 | 2 | 2 | 14 |
+-------+--------------+------+----------------+-------+
A simple SELECT round, table_number, game, pair_or_player, score FROM score outputs:
1|1|1|1|8
1|1|1|2|21
1|1|2|1|21
1|1|2|2|14
What I need is an additional column with 21 minus the score of the opponent of the same game (identified by the same round, table_number and game, and a different pair_or_player), the result of the respective query would be:
1|1|1|1|8 |0
1|1|1|2|21|13
1|1|2|1|21|7
1|1|2|2|14|0
How can this be done?
You need to inner join the table with itself.
(I'm not entirely sure what you mean by pair, but let's assume there's an ID which identifies each match, a match being composed of two records, one for the winner, one for the loser):
SELECT winners.pair, winners.score, losers.pair, losers.score
FROM (SELECT pair, score FROM score WHERE score = 21) as winners
INNER JOIN (SELECT pair, score FROM score WHERE score < 21) as losers
ON winners.match_id = losers.match_id;
I found it myself.
SELECT
a.round,
a.table_number,
a.game,
a.pair_or_player,
a.score,
21 - b.score
FROM
score AS a,
score AS b
WHERE
a.round = b.round
AND a.table_number = b.table_number
AND a.game = b.game
AND a.pair_or_player != b.pair_or_player
does the trick :-)