My sql questions about retrieving multiple values from different tables - mysql

MySQL tables:
author
(aEmail*
,fName
,lName
,bDate
,city
)
reviewer
(rEmail*
,phoneNumber
,lName
,fName
,city
)
paper
(paperId*
,title
,abstract
,submissionDate
)
author_paper
(authorId*
,paperId*
,isContact
)
paper_review
(paperId*
,reviewerId*
,score
,reviewSubmissionDate
,reviewInvitationDate
)
* = (component of) PRIMARY KEY
How would I find authors that have made more than 3 papers and return their names in a query and that the authors age is above a certain age (aEmail = authorId)

One option uses a correlated subquery for filtering: we can just count how many rows each author has in author_paper.
select a.*
from author a
where (select count(*) from author_paper ap where ap.authorid = a.id) > 3
I am unsure about the correlation clause. Maybe you want to use the author's email instead:
where ap.authorid = a.aemail

Related

what will be the sql query code of this "Healthcare" database?

SQL Query:
Write a SQL statement to display the SSN & name of patients who are younger than the average of all patients.
Write a SQL statement to display the name and age of the oldest patients along with his or her doctor's name.
write a SQL statement to display all of the patients who are prescribed with vitamins and below 50 years old.
display the name of most experienced heart specialist.
Solution: I have completed these answers of those questions but not sure it's okay or not. Please help me if there is any wrong or need any correction.
1.)
select SSN, Name
from patient
where age < (
select avg(age)
from Patient
);
2.)
select patient.name, max(patient.age), patient.DName
from Patient
join Doctor
where patient.primary_DoctorID = Doctor.DoctorID;
3.)
SELECT Patient.Name, Patient.age
from Patient
where patient.age < '50' and Patient.Primary_DoctorID = (
SELECT Prescription.DoctorID
from Prescription
where Prescription.P_ID = (
select Prescription_Medicine.P_ID
FROM Prescription_Medicine
where Prescription_Medicine.Tradename = 'Vitamin' )
);
Q3 returns
subquery returns more than one row
SELECT dname
FROM doctor
IN speciality = 'Heart' AND experience = (
SELECT MAX(doctor.experience)
FROM doctor
GROUP BY speciality;
);
Update your 3rd query to -
SELECT Patient.Name, Patient.age
from Patient
where patient.age < '50'
and EXISTS (
SELECT Prescription.DoctorID
from Prescription
where Patient.Primary_DoctorID = Prescription.DoctorID
and Prescription.P_ID = (
select Prescription_Medicine.P_ID
FROM Prescription_Medicine
where Prescription_Medicine.Tradename = 'Vitamin' )
);
And update your 4th query to -
SELECT dname
FROM doctor d
JOIN (SELECT speciality, MAX(doctor.experience)
FROM doctor
GROUP BY speciality) d1 on d.speciality = d1.speciality
WHERE speciality = 'Heart'
--USING MS-SQL SERVER FOR THE 4TH QUERY
--YOU CAN ALSO USE WINDOW FUNCTION TO SOLVE THE FOURTH
--QUERY.
--USING CTE (COMMON TABLE EXPRESSION)
WITH CTE
AS
(
SELECT
D.DNAME,
DENSE_RANK() OVER (PARTITION BY D.DNAME ORDER BY
D.EXPERIENCE DESC) AS RNK
FROM DOCTOR
WHERE SPECIALITY = 'HEART'
)
SELECT DNAME FROM CTE
WHERE RNK = 1

How to obtain a relevance score in a table

I have a mysql table that has typetags for games, like
table game_typetags:
name typetag
--------- --------
game#1 sports
game#1 soccer
game#2 race
game#2 sports
and another table games like
name playcount
--------- ---------
game#1 10
game#2 8
And I want to list relevant games (score them with number of common typetags) and sort with playcount.
So I'm trying to get such an array in PHP:
function getSimilarGames($typetags_array)
{
$pdo->query("SELECT * FROM games COUNT (*) AS ???? ORDER BY games.playcount DESC");
}
So that when I call this function with $typetags = [race,sports] , it should return
name score
____ _____
game#2 2
game#1 1
What query should I use for this?
Thanks !
I do not agree with your database design, you should try to normalize it a bit but here is code that would work on your design:
SELECT games.name, COUNT(1) score
FROM games
INNER JOIN game_typetags ON game_typetags.name = games.name
WHERE game_typetags.typetag IN ('race', 'sports')
GROUP BY games.name
ORDER BY score DESC
I suggest a design like so;
game
-------
id integer pk
name varchar
typetag
-------
id integer pk
tagname varchar
game_typetags
-------
game_id int pk
typetag_id int pk
With this design you join together your tables using ID's instead of using strings everywhere.
A query to get the data you want from this could look like this, note that you could even use the tag-ids to make it tad faster (working on primary key in the database instead of string comparison in the where clause)
SELECT game.id, game.name, game.playcount, COUNT(1) score
FROM game
INNER JOIN game_typetags gtt ON gtt.game_id = game.id
INNER JOIN typetag tt ON tt.id = gtt.typetag_id
WHERE tt.typetag IN ('race', 'sports')
GROUP BY game.id
ORDER BY score DESC
And I figured out a solution to this problem, as follows:
SELECT name, SUM(matches) as relevance_score FROM
(
SELECT
game_typetags.*,
(typetag = 'race' )
+ (typetag = 'sports' )
AS matches
FROM game_typetags HAVING matches>0
ORDER BY matches DESC
) a GROUP BY name ;

Query to return Pokemon with highest attack by group

I have a database full of Pokemon Cards, and their attacks. I want to do a query to find the Pokemon that has the strongest attack by each type. I want the view to show just the name, type, and damage of the attack.
SELECT p2.MaxD, p2.Type, p1.name
FROM Pokemon p1
INNER JOIN ( SELECT type, MAX(damage) MaxD, pokemon_name FROM Attack GROUP BY Type )
p2 ON p1.type = p2.type AND p2.pokemon_name = p1.name
I have this code. It returns the highest damage but not the correct Pokemon. The Pokemon table doesn't have a damage field. I'm trying to get a grasp of joins.
Here is the structure:
Attack table has 4 fields: pokemon_name (the pokemon this attack belongs to), damage, name (name of the attack), and type (the type of pokemon this attack belongs to).
The Pokemon table has 3: HP, type (of the pokemon), and name (of the pokemon).
First of all you have to build select that select maximal damage for each type (you already have that):
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
Now, this won't have a good performance unless:
type is INT (or ENUM or other numeric type)
there's index on type or type, damage
You cannot select pokemon_name because MySQL doesn't guarantee that you'll get pokemon_name matching MaxD (here's a nice answer on stackoverflow which already covers this issue).
Now you can select pokemon with that matching pokemon_name
SELECT p1.pokemon_name, p1.type, p1.damage
FROM Attack p1
INNER JOIN (
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
) p2 ON p1.type = p2.type
AND p1.damage = p2.MaxDamage
GROUP BY (p1.type, p1.damage)
The last GROUP BY statement makes sure that having multiple pokemons with the same attack damage won't cause multiple records for one type,damage pairs.
Again, you will achieve good performance by replacing pokemon_name with pokemon_id. Maybe you should google database normalization for a while [wikipedia],[first tutorial]. You also may want to check this Q&A out, it provides nice overview of what does "relation table" mean.
Now you have correct pokemon_name (for your program sake, I hope you'll replace this with pokemon_id) and you may put it all together:
SELECT p1.pokemon_name, p1.type, p1.damage, p.*
FROM Attack p1
INNER JOIN (
SELECT type, MAX(damage) MaxD FROM Attack GROUP BY Type
) p2 ON p1.type = p2.type
AND p1.damage = p2.MaxDamage
INNER JOIN Pokemon p
ON p.pokemon_name = p1.pokemon_name
GROUP BY (p1.type, p1.damage)
Ideal example
In perfect world you're database would look like this:
-- Table with pokemons
CREATE TABLE `pokemons` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255),
-- More fields
PRIMARY KEY (`id`)
)
-- This contains pairs as (1,'Wather'), (2, 'Flame'), ...
CREATE TABLE `AttackTypes` (
`id`,
`name` VARCHAR(255)
)
-- Create records like (1, 2, 3, 152)
-- 1 = automatically generated keys
-- 2 = id of pokemon (let say it's Pikachu :P)
-- 3 = type of attack (this say it's Electric)
-- 152 = damage
-- This way each pokemon may have multiple attack types (Charizard flame + wind)
CREATE TABLE `Attacks` (
`id`,
`pokemonID` INT NOT NULL, -- Represents pokemons.id
`typeID` INT NOT NULL, -- Represents attack.id
`damage` INT
)
ID fields are ALWAYS PRIMARY KEY, NOT NULL and AUTO_INCREMENT in this example
And the select from it, again get types first:
SELECT MAX(attack.damage) AS mDmg, attack.typeID
FROM attack
GROUP BY attack.typeID
Than get pokemon ID:
SELECT a.pokemonID, a.damage, a.typeID
FROM attack AS a
INNER JOIN (
SELECT MAX(a.damage) AS mDmg, a.typeID
FROM attack AS a
GROUP BY a.typeID
) AS maxA
ON a.typeID = maxA.typeID
AND a.damage = mDmg
GROUP BY (a.typeID)
And once you've covered all that you may actually select pokemon data
SELECT aMax.pokemonID as id,
aMax.damage,
p.name AS pokemonName,
aMax.typeID AS attackTypeID,
t.name AS attackType
FROM (
SELECT a.pokemonID, a.damage, a.type
FROM attack AS a
INNER JOIN (
SELECT MAX(a.damage) AS mDmg, a.type
FROM attack AS a
GROUP BY a.type
) AS maxA
ON a.type = maxA.type
AND a.damage = mDmg
GROUP BY (a.type)
) AS aMax
INNER JOIN pokemons AS p
ON p.id = aMax.pokemonID
INNER JOIN AttackTypes AS t
ON t.id = aMax.typeID
Performance hints:
you may add field MaxDamage into AttackTypes (which would be calculated by stored procedure) and will save you one level of nasted query
all ID fields should be PRIMARY KEYs
index on Attacks.typeID allows you to quickly get all pokemons capable of that type of attack
index on Attack.damage allows you to quickly find strongest attack
index on Attack.type, Attack.damage (two fields) will be helpful when finding max value for each attack
index on Attack.pokemonID will make look up pokemon -> attack -> attack type name faster
I'm not really sure about your schema but I assumed that the pokemon_name of your attack table is really the name of the pokemon.
SELECT a.*, c.*
FROM Attack a
INNER JOIN
(
SELECT type, MAX(damage) MaxD
FROM Attack
GROUP BY Type
) b ON a.Type = b.Type AND
a.damage = b.MaxD
INNER JOIN Pokemon c
ON c.Name = a.pokemon_name AND
c.Type = a.Type
the above query displays all field from attack table and pokemon table, but If you are really interested on the name, damage and type the you only do query on attack table
SELECT a.*
FROM Attack a
INNER JOIN
(
SELECT type, MAX(damage) MaxD
FROM Attack
GROUP BY Type
) b ON a.Type = b.Type AND
a.damage = b.MaxD

How do you do multiple inserts that have selects

How do I do an insert multiple values or records that have to get their information from select statements. This doesn't work.
INSERT INTO marriedcouples (male,female) VALUES (
(SELECT id FROM men WHERE username='brad',
SELECT id FROM women WHERE username='jennifer')
(SELECT id FROM men WHERE username='ken',
SELECT id FROM women WHERE username='barbie'))
Assuming I have tables with:
men(id,name), women(id,name), couples(id,male,female)
etc.
Thanks,
Dan
Insert marriedcouples( male, female )
Select M.id, W.id
From Men As M
Cross Join Women As W
Where ( M.username = 'brad' And W.username = 'jennifer' )
Or ( M.username = 'ken' And W.username = 'barbie' )
Addition
In comments, you asked specifically about the problems with your original query. First, you could have used your original approach like so:
Insert marriedcouples( male, female )
Select ( Select Id From men Where username = 'brad' )
, ( Select Id From women Where username = 'jennifer' )
Union All
Select ( Select Id From men Where username = 'ken' )
, ( Select Id From women Where username = 'barbie' )
Notice that each value is enclosed in parentheses as its own encapsulated subquery. Second, notice that I used the Union All directive to allow me to stack the two queries and give me two rows. Third, notice that I'm not trying to use the Values directive in combination with subqueries. You can use the Values clause to list out values or you can use a Select statement but not both in the way you did. Obviously, this approach of four subqueries will not perform well but it helps to understand the breakdown of the syntax.

MySQL Query: select all the books a user has completed reviewing

I have the following 5 tables:
users(user_id)
books(book_id, author_id)
source_phrases(source_phrase_id, book_id, phrase)
synonym_phrases(synonym_phrase_id, source_phrase_id, reader_id, synonym)
synonym_ratings(synonym_ratings_id, synonym_phrase_id, rater_id, rating)
I am trying to get a query that will select all the books a user has completed reviewing.
A user will have completed reviewing a book if they have done the following for each source phrase:
User has suggested a synonym for the source phrase (reader_id in synonym_phrases table is the users id)
OR
The user has rated a synonym for the source phrase (rater_id in synonym_ratings table is the users id)
SELECT b.*
FROM books b
WHERE book_id NOT IN
(
SELECT sp.book_id
FROM source_phrases sp
WHERE source_phrase_id NOT IN
(
SELECT syp.source_phrase_id
FROM synonym_phrases syp
WHERE reader_id = #user_id
)
AND source_phrase_id NOT IN
(
SELECT syp.source_phrase_id
FROM synonym_phrases syp
JOIN synonym_ratings sr
ON sr.synonym_phrase_id = syp.synonym_phrase_id
AND sr.rater_id = #user_id
)
)
AND book_id IN
(
SELECT sp.book_id
FROM source_phrases sp
)