SQL queries, can't figure out how this query can work - sql

How can you select max(table column), and on the from clause declare another table column? It doesn't make any sense - can anyone explain?
SELECT
Ord.SalesOrderID, Ord.OrderDate,
(SELECT MAX(OrdDet.UnitPrice)
FROM Sales.SalesOrderDetail AS OrdDet
WHERE Ord.SalesOrderID = OrdDet.SalesOrderID) AS MaxUnitPrice
FROM
Sales.SalesOrderHeader AS Ord;

A sql SELECT query always returns what’s called a “recordset”, no matter how simple or fancy the query is. For example, this will return a “recordset” with 2 columns and however many rows are in the table BIRDS matching the name “Bluebird” or “Cardinal”:
SELECT NAME, COLOR FROM BIRDS WHERE NAME = ‘Bluebird’ OR NAME = ‘Cardinal’
Now, if you wanted to further narrow down what you’re querying the database for, sql lets you “query the query”, if that’s what you want to do:
SELECT * FROM (SELECT NAME, COLOR FROM BIRDS WHERE NAME = ‘Bluebird’ OR NAME = ‘Cardinal’) WHERE COLOR = ‘Blue’ OR COLOR = ‘Yellow’
^^^ This second query uses the recordset from the first query in place of a table.
This “inner query” is called a “subquery”.
You can also use subqueries in individual columns, like so:
SELECT A.NAME, (SELECT AVG(POPULATION) FROM BIRDS B WHERE B.COLOR = A.COLOR) AVGPOP FROM BIRDS A
^^^ This third query uses a subquery to fetch the average population of birds matching the color of each bird returned in the “outer” query. So, the final recordset returned will have two columns: “NAME” and “AVGPOP”.

Related

select rows where value is smaller than the value of a specific row

Hi I am new to SQL and have a question.
If I want to select all cities from a table of which the population is smaller than NYC (NYC is also in the table).
I can code like this
SELECT city FROM table where Population <= (select population from table where name = 'NYC')
My question is whether I can write it more concisely like
where city.population < NYC.population
No, you cannot write it more concisely that way, because NYC is not defined.
You can, however, also write this as a join:
SELECT t.city
FROM table t JOIN
table NYC
ON t.population < NYC.population AND
NYC.name = 'NYC';
It is not really more concise. And the difference between the two is really a matter of preference for this particular problem.

When is aliasing required when using SQL set theory clauses?

I just started learning SQL and am trying to learn from my mistakes. In one of my practice exercises, I had to find city names from the cities database are not listed as capital cities in countries database. Initially I tried the code below but it yielded an error.
SELECT name
FROM cities
EXCEPT
SELECT capital
FROM countries
ORDER BY capital ASC;
The correct code is:
SELECT city.name
FROM cities AS city
EXCEPT
SELECT country.capital
FROM countries AS country
ORDER BY name;
Can someone explain to me why aliasing made all the difference here?
An ORDER BY for a UNION, EXCEPT or INTERSECT sorts the complete result. The column names of the overall query are defined by the first query. So this query:
SELECT name
FROM cities
EXCEPT
SELECT capital
FROM countries
returns a result with a single column named name.
Adding an order by is conceptually the same as:
select *
from (
SELECT name
FROM cities
EXCEPT
SELECT capital
FROM countries
) x
order by ....;
As the inner query only returns a single column name, that's the only column you can use in the order by.
The aliases that you used in your second query don't change the column name of the overall result which determines the column names available for the order by clause.

Solving a SQL query involving set division by another method

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);

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

Return all Fields and Distinct Rows

Whats the best way to do this, when looking for distinct rows?
SELECT DISTINCT name, address
FROM table;
I still want to return all fields, ie address1, city etc but not include them in the DISTINCT row check.
Then you have to decide what to do when there are multiple rows with the same value for the column you want the distinct check to check against, but with different val;ues in the other columns. In this case how does the query processor know which of the multiple values in the other columns to output, if you don't care, then just write a group by on the distinct column, with Min(), or Max() on all the other ones..
EDIT: I agree with comments from others that as long as you have multiple dependant columns in the same table (e.g., Address1, Address2, City, State ) That this approach is going to give you mixed (and therefore inconsistent ) results. If each column attribute in the table is independant ( if addresses are all in an Address Table and only an AddressId is in this table) then it's not as significant an issue... cause at least all the columns from a join to the Address table will generate datea for the same address, but you are still getting a more or less random selection of one of the set of multiple addresses...
This will not mix and match your city, state, etc. and should give you the last one added even:
select b.*
from (
select max(id) id, Name, Address
from table a
group by Name, Address) as a
inner join table b
on a.id = b.id
When you have a mixed set of fields, some of which you want to be DISTINCT and others that you just want to appear, you require an aggregate query rather than DISTINCT. DISTINCT is only for returning single copies of identical fieldsets. Something like this might work:
SELECT name,
GROUP_CONCAT(DISTINCT address) AS addresses,
GROUP_CONCAT(DISTINCT city) AS cities
FROM the_table
GROUP BY name;
The above will get one row for each name. addresses contains a comma delimted string of all the addresses for that name once. cities does the sames for all the cities.
However, I don't see how the results of this query are going to be useful. It will be impossible to tell which address belongs to which city.
If, as is often the case, you are trying to create a query that will output rows in the format you require for presentation, you're much better off accepting multiple rows and then processing the query results in your application layer.
I don't think you can do this because it doesn't really make sense.
name | address | city | etc...
abc | 123 | def | ...
abc | 123 | hij | ...
if you were to include city, but not have it as part of the distinct clause, the value of city would be unpredictable unless you did something like Max(city).
You can do
SELECT DISTINCT Name, Address, Max (Address1), Max (City)
FROM table
Use #JBrooks answer below. He has a better answer.
Return all Fields and Distinct Rows
If you're using SQL Server 2005 or above you can use the RowNumber function. This will get you the row with the lowest ID for each name. If you want to 'group' by more columns, add them in the PARTITION BY section of the RowNumber.
SELECT id, Name, Address, ...
(select id, Name, Address, ...,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY id) AS RowNo
from table) sub
WHERE RowNo = 1