Solving a SQL query involving set division by another method - sql

The problem statement for the SQL query is to find all the pilots who can fly the planes listed in the Plane table. So basically we have a Pilot table which has 2 columns namely Pilot Names and the Planes they can fly and the second table called the Plane table which has only 1 column namely the Planes column. So, we have to find all the pilots that can fly all the planes listed in the Plane column of the Plane table. I know one of the ways is to proceed via relational division but is there any other way to solve this?
The Table Schema loos like this:
Pilot(Pilot_Name, Planes)
Plane(Planes)
That shows the Pilot table consists of 2 columns and the Plane table consist of a single column.
The below code is the solution to the problem using set divison:
SELECT DISTINCT pilot_name
FROM PilotSkills AS PS1
WHERE NOT EXISTS
(SELECT *
FROM Hangar
WHERE NOT EXISTS
(SELECT *
FROM PilotSkills AS PS2
WHERE (PS1.pilot_name = PS2.pilot_name)
AND (PS2.plane_name = Hangar.plane_name)));
This query gives the desired result. But I was wondering if there is another method to solve this question without using the above mentioned concept of set division.

I would do this using aggregation:
select pilot_name
from pilotskills
group by pilot_name
having count(*) = (select count(*) from planes);
If a plan could be listed twice in for a given pilot in the skills table, the having clause should be:
select pilot_name
from pilotskills
group by pilot_name
having count(distinct plane_name) = (select count(*) from planes);

Related

Can we select data from 2 tables in same time

From the code as shown below. I wonder that why it can select data from 2 tables in same time that are table "venue AS v" and table "as s".
or I misunderstand?
SELECT name, s.num_match
FROM venue AS v,
(SELECT venue_id, COUNT(*) AS num_match
FROM match
GROUP BY venue_id) AS s
WHERE v.venue_id = s.venue_id
LIMIT 3;
Yes you can using JOIN clause for example.
More infos here: https://www.informit.com/articles/article.aspx?p=30875&seqNum=5
Yes you can select data from as many as tables you want at the same time.
In this case you are trying to get an aggregated number from table-s and join it with the table v.
There are many ways to write the code to join the table. Above is one method which you have used.

Add / update column from a query SELECT? SQL

I'm quite a novice on this and I don't know if I will explain myself well. I am trying to do an exercise in SQL in which asks me to update the data in an "X" table from other data in a "Y" table. The problem is that it is not about updating table X exactly like the data in table Y. I put the statement and my tables:
Update the "numJocs" field (number of games) for all platforms, depending on the number of games each of the platforms in the GAMES table has.
PLATFORM table:
where: "nom" is name.
GAMES table:
where: "nom" is name, "preu" is price, "idPlataforma" is idPlatform and "codiTenda" is storeCode, but only idPlataforma interested for this exercise.
If I do:
SELECT COUNT(games.idPlataforma)
FROM games
GROUP BY (games.idPlataforma)
I can see how many games there are for each platform. The result would be:
count(games.idPlataforma)
__________________________
2
1
2
2
I would like to be able to put this result in the PLATFORM table, column "numJocs". But I don't know how to do it ... I also don't want to put it manually, that is, a "2" in a row "1", etc ... but I would like to be able to make a query and add that query in the column that I have to fill in. He tried to do a thousand things, but nothing ... Any help?
Thanks!!
for one time update you can use below query
update Product P
INNER JOIN (
SELECT games.idPlataforma, COUNT(games.idPlataforma) as cnt
FROM games
GROUP BY games.idPlataforma
) x ON P.id= x.idPlataforma
SET P.numJocs= x.cnt
For the next time on every entry of new game you have a update numJocs
Suppose you have 2 tables, table 1 and table 2:
Table 1:
Table 2:
You could insert new values into table 1, based on table 2 by doing the following:
insert into Table1(number,CFG) select ITEM,results from Table2
Which has the following result in table 1:
Any database should support the syntax using a correlated subquery:
update platforms
set numjocs = (select count(*)
from games g
where g.idPlatforms = platforms.id
);
I would caution you though about storing this value in the table. It will be immediately out of data if the platforms table changes. If you want to keep it in synch, then you need to create triggers -- and this is all rather complicated.
Rather, calculate the data on the fly:
select p.*,
(select count(*)
from games g
where g.idPlatforms = platforms.id
) as numjocs
from platforms p ;
You can put this in a view if you like. Many databases support materialized views where the results of the view are stored in a "table" and the table is kept up-to-date with the underlying data.

How to include zero results when querying one single table?

I have a table called Apartments that has three columns: apartment_type, person, date. It includes the apartment type selected by a certain person and date. I need to count how many people picked each of the apartment types. Some apartment type have 0 population.
Here is my query:
SELECT apartment_type, COUNT(*) AS TOTAL
FROM Apartments
GROUP BY apartment_type
It works great, but it doesn't include apartment types with a value of 0. Please, help me to correct this query.
In case some appartment_type have 0 population - your table will not contain any record with that type - so you must add some join from another table, where all apartment types exists. Or use union to create all 0 populated entries.
Something like:
SELECT apartment_type, COUNT(*) AS TOTAL
FROM (SELECT * FROM Apartments UNION ALL SELECT apartment_type, 0 as person, 0 as date from SomeTableWithFullListOfTypes group by apartment_type) as tmp
GROUP BY apartment_type
I generally agree with Nosyara's answer, but I don't agree with his sample query with the union all. I'm not sure it works, and it's certainly too complicated.
As stated already, if you don't have a table with all the possible apartment types, create one. Then you can write your query using a simple left join:
select t.apartment_type, count(a.apartment_type) as total
from apartment_types t
left join apartments a
on a.apartment_type = t.apartment_type
group by t.apartment_type
Note how count(*) was replaced by count(a.apartment_type). That change is necessary to have an accurate count in the case where you don't have apartments for a certain apartment type.
SELECT apartment_type, COUNT(apartment.*) AS TOTAL
FROM apartment_type
left join apartment
on apartment_type.aparentment_type = apartements.apartment_type
GROUP BY apartment_type
Using a left join will give you everything from the left side of the join (so all your types) and anything from the right that matches.

How to use the result from a second select in my first select

I am trying to use a second SELECT to get some ID, then use that ID in a second SELECT and I have no idea how.
SELECT Employee.Name
FROM Emplyee, Employment
WHERE x = Employment.DistributionID
(SELECT Distribution.DistributionID FROM Distribution
WHERE Distribution.Location = 'California') AS x
This post got long, but here is a short "tip"
While the syntax of my select is bad, the logic is not. I need that "x" somehow. Thus the second select is the most important. Then I have to use that "x" within the first select. I just don't know how
/Tip
This is the only thing I could imagine, I'm very new at Sql, I think I need a book before practicing, but now that I've started I'd like to finish my small program.
EDIT:
Ok I looked up joins, still don't get it
SELECT Employee.Name
FROM Emplyee, Employment
WHERE x = Employment.DistributionID
LEFT JOIN Distribution ON
(SELECT Distribution.DistributionID FROM Distribution
WHERE Distribution.Location = 'California') AS x
Get error msg at AS and Left
I use name to find ID from upper red, I use the ID I find FROM upper red in lower table. Then I match the ID I find with Green. I use Green ID to find corresponding Name
I have California as output data from C#. I want to use California to find the DistributionID. I use the DistributionID to find the EmployeeID. I use EmployeeID to find Name
My logic:
Parameter: Distribution.Name (from C#)
Find DistributionID that has Distribution.Name
Look in Employment WHERE given DistributionID
reveals Employees that I am looking for (BY ID)
Use that ID to find Name
return Name
Tables:
NOTE: In this example picture the Employee repeats because of the select, they are in fact singular
In "Locatie" (middle table) is Location, I get location (again) from C#, I use California as an example. I need to find the ID first and foremost!
Sory they are not in english, but here are the create tables:
Try this:
SELECT angajati.Nume
FROM angajati
JOIN angajari ON angajati.AngajatID = angajari.AngajatID
JOIN distribuire ON angajari.distribuireid = distribuire.distribuireid
WHERE distribuire.locatie = 'california'
As you have a table mapping employees to their distribution locations, you just need to join that one in the middle to create the mapping. You can use variables if you like for the WHERE clause so that you can call this as a stored procedure or whatever you need from the output of your C# code.
Try this solution:
DECLARE #pLocatie VARCHAR(40)='Alba'; -- p=parameter
SELECT a.AngajatID, a.Nume
FROM Angajati a
JOIN Angajari j ON a.AngajatID=j.AngajatID
JOIN Distribuire d ON j.DistribuireID=d.DistribuireID
WHERE d.Locatie=#pLocatie
You should add an unique key on Angajari table (Employment) thus:
ALTER TABLE Angajari
ADD CONSTRAINT IUN_Angajari_AngajatID_DistribuireID UNIQUE (AngajatUD, DistribuireID);
This will prevent duplicated (AngajatID, DistribuireID).
I don't know how you are connecting Emplyee(sic?) and Employment, but you want to use a join to connect two tables and in the join specify how the tables are related. Joins usually look best when they have aliases so you don't have to repeat the entire table name. The following query will get you all the information from both Employment and Distribution tables where the distribution location is equal to california. You can join employee to employment to get name as well.
SELECT *
FROM Employment e
JOIN Distribution d on d.DistributionID = e.DistributionID
WHERE d.Location = 'California'
This will return the contents of both tables. To select particular records use the alias.[Col_Name] separated by a comma in the select statement, like d.DistributionID to return the DistributionID from the Distribution Table

Help me understand this particular use of nested SELECT statements

From this site:
Tables:
CREATE TABLE PilotSkills
(pilot_name CHAR(15) NOT NULL,
plane_name CHAR(15) NOT NULL,
PRIMARY KEY (pilot_name, plane_name));
CREATE TABLE Hangar
(plane_name CHAR(15) NOT NULL PRIMARY KEY);
Query:
SELECT DISTINCT pilot_name
FROM PilotSkills AS PS1
WHERE NOT EXISTS
(SELECT *
FROM Hangar
WHERE NOT EXISTS
(SELECT *
FROM PilotSkills AS PS2
WHERE (PS1.pilot_name = PS2.pilot_name)
AND (PS2.plane_name = Hangar.plane_name)));
I understand the problem it's used for (set division), including the analogy that describes it as "There ain't no planes in this hangar that I can't fly!". What I don't understand is exactly what's at work here, and how it comes together to do what it says its doing.
Having trouble stating with specifics my difficulty at the moment...
Edit: Let me just first ask what something like this does, exactly:
SELECT DISTINCT pilot_name
FROM PilotSkills
WHERE NOT EXISTS
(SELECT *
FROM Hangar)
I think I'm missing some fundamental understanding here...
Edit: Irrelevant, and it wouldn't be a meaningful without the third nested SELECT, right?
What we want is a distinct list of pilots that can fly every plane in the hanger. For that to be true, for a given pilot, there cannot exist a plane they cannot fly. So, we want to get a list of all planes for each pilot and see if there is one they cannot fly. If there is one (the pilot cannot fly) we remove them from the list. Whomever is left, can fly all planes in the hanger.
Said more formally, find a distinct list of pilot names such that for a given pilot, there does not exist a plane in the set of planes (Hanger) such that the plane does not exist in the set of the given pilot's skills.
"find a distinct list of pilot
names..."
Select Distinct pilot_name
From PilotSkills As PS1
...
"...such that for a given pilot, there
does not exist a plane in the set of
planes (Hanger)..."
Select Distinct pilot_name
From PilotSkills As PS1
Where Not Exists (
Select 1
From Hanger
"...such that the plane does not exist
in the set of the given pilot's
skills."
Select Distinct pilot_name
From PilotSkills As PS1
Where Not Exists (
Select 1
From Hanger As H
Where Not Exists (
Select 1
From PilotSkills As PS2
Where PS2.pilot_name = PS1.pilot_name
And PS2.plane_name = H.plane_name
)
)
As a minor comment initially, Select * is overkill in this situation. You should select a single column, or a couple of columns, but pulling all columns should be avoided, especially in sub queries where they're only used during the query and not returned in the final result set. That said, to try to break down the work flow:
Select Pilot_Name From PilotSkills - We're interested in pilot names eventually.
Where Not Exists (Select * From Hangar) - We're only going to retrieve pilots if there is not a relevant entry for them in the Hangar table.
Where Not Exists (Select * From PilotSkills) - We're only going to retrieve Hangars that don't have a pilot from the outer query.
Describing it as a double negative (from the other answer) is a great way to understand it. It can probably be achieved more directly.
Conceptually it is just a double negative.
Select all the pilots for which there
does not exist a plane in the hangar
that they cannot fly.
But it seems you are asking about the mechanics of the query itself? It uses two levels of correlated sub query.
If we reduce the number of rows down to a minimal amount and add an additional table to simplify the explanation slightly (the outer instance of PilotSkills in the query is just used to get the list of Pilots). Then the query would look like
SELECT pilot_name
FROM Pilots
WHERE NOT EXISTS
(SELECT *
FROM Hangar
WHERE NOT EXISTS
(SELECT *
FROM PilotSkills
WHERE (Pilots.pilot_name = PilotSkills.pilot_name)
AND (PilotSkills.plane_name = Hangar.plane_name)));
Pilots
pilot_name
===========
'Celko'
'Higgins'
Hangar
plane_name
=============
'B-1 Bomber'
'F-14 Fighter'
PilotSkills
pilot_name plane_name
=========================
'Celko' 'F-14 Fighter'
'Higgins' 'B-1 Bomber'
'Higgins' 'F-14 Fighter'
If you want to know which pilots can fly all the planes in the hangar then
For each Pilots.pilot_name in turn
Look at each Hangar.plane_name in turn
And check if there is a corresponding row in PilotSkills for that pilot_name,plane_name
If step 3 is false then we know that there is at least one plane in the hangar the pilot cannot fly and we can stop processing that Pilots row and go onto the next one. If step 3 is true then we must then return to step 2 and check the next plane in the Hangar. If we finish processing all planes in the hangar and for each one there has been a corresponding row in PilotSkills then we know that this pilot can fly all planes.
Or to put it another way we know there does not exist a plane (as we have checked them all) for which there does not exist a matching row in the PilotSkills table.