For an assignment I have to do certain consults from a db.
I need to find all the moves that the pokemon "bulbasaur" has and list their names, power, pp and accuracy.
I've tried this command and I get only one move out of 9 as a result:
SELECT name, power, pp, accuracy
FROM MOVES
WHERE id=(SELECT move_id FROM POKEMONS_MOVES WHERE pokemon_id=(SELECT id
FROM POKEMONS WHERE name="bulbasaur"));
vine-whip|45|25|100
I am using sqlite3 btw. Thanks in advance.
Since there is a CrossRef table in between, this can be easily achieved through Joins.
SELECT
m.name, m.type_id, m.power, m.pp, m.accuracy
FROM
Moves m
INNER JOIN
Pokemons_Moves pm ON pm.move_id = m.id
INNER JOIN
Pokemons p ON p.id = pm.pokemon_id
WHERE
p.name = "bulbasaur"
You are using scalar subqueries:
WHERE id=(SELECT ...)
This means that if the subquery returns more than one row, the database ignores all rows but the first. (Other databases will error out if that happens.)
To search through all IDs returned by the subquery, you have to use the IN operator:
SELECT name, power, pp, accuracy
FROM Moves
WHERE id IN (SELECT move_id
FROM Pokemons_Moves
WHERE pokemon_id = (SELECT id
FROM Pokemons
WHERE name = 'bulbasaur'));
(The second subquery returns only one value.)
Related
Background
I've got this PostgreSQL join that works pretty well for me:
select m.id,
m.zodiac_sign,
m.favorite_color,
m.state,
c.combined_id
from people."People" m
LEFT JOIN people.person_to_person_composite_crosstable c on m.id = c.id
As you can see, I'm joining two tables to bring in a combined_id, which I need for later analysis elsewhere.
The Goal
I'd like to write a query that does so by picking the combined_id that's got the lowest value of m.id next to it (along with the other variables too). This ought to result in a new table with unique/distinct values of combined_id.
The Problem
The issue is that the current query returns ~300 records, but I need it to return ~100. Why? Each combined_id has, on average, 3 different m.id's. I don't actually care about the m.id's; I care about getting a unique combined_id. Because of this, I decided that a good "selection criterion" would be to select rows based on the lowest value m.id for rows with the same combined_id.
What I've tried
I've consulted several posts on this and I feel like I'm fairly close. See for instance this one or this one. This other one does exactly what I need (with MAX instead of MIN) but he's asking for it in Unix Bash 😞
Here's an example of something I've tried:
select m.id,
m.zodiac_sign,
m.favorite_color,
m.state,
c.combined_id
from people."People" m
LEFT JOIN people.person_to_person_composite_crosstable c on m.id = c.id
WHERE m.id IN (select min(m.id))
This returns the error ERROR: aggregate functions are not allowed in WHERE.
Any ideas?
Postgres's DISTINCT ON is probably the best approach here:
SELECT DISTINCT ON (c.combined_id)
m.id,
m.zodiac_sign,
m.favorite_color,
m.state,
c.combined_id
FROM people."People" m
LEFT JOIN people.person_to_person_composite_crosstable c
ON m.id = c.id
ORDER BY
c.combined_id,
m.id;
As for performance, the following index on the crosstable might speed up the query:
CREATE INDEX idx ON people.person_to_person_composite_crosstable (id, combined_id);
If used, the above index should let the join happen faster. Note that I cover the combined_id column, which is required by the select.
I want to connect table with lessons with table with students
Here is my query:
SELECT *
FROM Student
JOIN (
SELECT *
FROM Lesson
JOIN Has on Has.Diid = Lesson.Diid
and Has.Name = Lesson.Name
) m
on m.CreditBookNumber = Student.CreditBookNumber
But it doesn't work. Is there a way to rewrite this?
[![enter image description here][1]][1]
First off, I would avoid the subquery here. It's superfluous at best and could cause errors to be thrown at worst. The biggest opportunity for Error that I see is that in your subquery's result set there will be two columns with the same name diid. You'll need to alias those in order for them to pick up two different names and not toss an error.
Consider instead:
SELECT *
FROM Student
JOIN Has ON
Student.CreditBookNumber = Has.CreditBookNumber
JOIN Lesson ON
Has.Diid = Lesson.Diid
and Has.Name = Lesson.Name
If you are bound and determined to do this in a subquery then you will need to list out each column and provide an alias for columns where the name collides.
SELECT *
FROM Student
JOIN (
SELECT Lesson.*,
Has.Diid as HasDiid,
Has.Name as HasName,
Has.CreditBookNumber,
Has.Gid
FROM Lesson
JOIN Has on Has.Diid = Lesson.Diid
and Has.Name = Lesson.Name
) m
on m.CreditBookNumber = Student.CreditBookNumber
But since this much more complex sql will produce identical results as the first sql above, it's really overkill.
SELECT Title
FROM movie
WHERE Movie_no = (SELECT Movie_no
FROM Customer
INNER JOIN issues ON customer.`Cus_id`=issues.`Cus_id`
WHERE NAME = 'Shyam')
No. You just need to use IN or ANY:
SELECT Title
FROM movie
WHERE Movie_no IN (SELECT Movie_no
FROM Customer INNER JOIN
issues
ON customer.Cus_id = issues.Cus_id
WHERE NAME = 'Shyam'
);
I mean, you might need to normalize your data for other reasons, but a subquery returning more than one value would not be such a reason. And, your data structure seems reasonable, based on this one query.
SELECT state, business, a.report
FROM base
WHERE state IN
(SELECT a.state FROM heart a join (SELECT CAST(MAX(percent_adults) AS DOUBLE) max1 FROM heart)b on (a.percent_adults=b.max1));
In the above subquery, only one value can be returned i.e a.state from table 'heart'.
that value is used in the main query and fetches business from 'base' table . I need to return a.report from 'heart' table in subquery in the report along with state and business . Thanks much!
Use exists instead of in:
SELECT state, business, a.report
FROM base b
WHERE EXISTS (SELECT 1
FROM heart h JOIN
(SELECT MAX(percent_adults) as max1
FROM heart h2
) sb
ON h.percent_adults = h2.max1
WHERE h.state = b.state and h.business = b.business
);
Don't convert the maximum value, particularly to a floating point representation. It is generally dangerous to compare floating point values for equality, but it should be safe with the result of a MIN() or MAX().
You don't need the subquery to return two values. All you need the subquery for is to provided the unique join value (relying on the odds that two calculated double values will ever be exactly the same).
So just do a join and choose the report from that:
select b.state, b.business, a.report
from base b
join heart a
on a.state = b.state
where a.percent_adults =(
select max( percent_adults )
from heart );
You're limiting the join to just the one state with the highest percent_adult value and getting the report from that.
See Here Is it possible for a subquery to return two values?
You need to have the subquery in the FROM clause to do this.
Outside of defining the subquery twice, I am not sure there is a more efficient way to accomplish this with subqueries.
However, perhaps a JOIN would be more useful here:
SELECT a.state, business, a.report
FROM base b
JOIN heart a ON (a.percent_adults = b.max1 AND b.state = a.state)
I am attempting to get the information from one table (games) and count the entries in another table (tickets) that correspond to each entry in the first. I want each entry in the first table to be returned even if there aren't any entries in the second. My query is as follows:
SELECT g.*, count(*)
FROM games g, tickets t
WHERE (t.game_number = g.game_number
OR NOT EXISTS (SELECT * FROM tickets t2 WHERE t2.game_number=g.game_number))
GROUP BY t.game_number;
What am I doing wrong?
You need to do a left-join:
SELECT g.Game_Number, g.PutColumnsHere, count(t.Game_Number)
FROM games g
LEFT JOIN tickets t ON g.Game_Number = t.Game_Number
GROUP BY g.Game_Number, g.PutColumnsHere
Alternatively, I think this is a little clearer with a correlated subquery:
SELECT g.Game_Number, G.PutColumnsHere,
(SELECT COUNT(*) FROM Tickets T WHERE t.Game_Number = g.Game_Number) Tickets_Count
FROM Games g
Just make sure you check the query plan to confirm that the optimizer interprets this well.
You need to learn more about how to use joins in SQL:
SELECT g.*, count(*)
FROM games g
LEFT OUTER JOIN tickets t
USING (game_number)
GROUP BY g.game_number;
Note that unlike some database brands, MySQL permits you to list many columns in the select-list even if you only GROUP BY their primary key. As long as the columns in your select-list are functionally dependent on the GROUP BY column, the result is unambiguous.
Other brands of database (Microsoft, Firebird, etc.) give you an error if you list any columns in the select-list without including them in GROUP BY or in an aggregate function.
"FROM games g, tickets t" is the problem line. This performs an inner join. Any where clause can't add on to this. I think you want a LEFT OUTER JOIN.