How do I find the name of someone who has multiple entries in a different table - sql

I'm trying to figure out how to get through this type of scenario:
I have a Table A with names of people along with their IDs. I have a Table B with the people's IDs and the type of car that they bought from this dealership. Multiple people can have multiple entries if they buy multiple cars.
Let's say that David with ID 789 has bought multiple cars from this dealership, so he has multiple entries in Table B:
(ID | Car)
(----|----)
(789 | Toyota)
(789 | Ford)
I want to query these tables so that my results show all of the people who have bought a Toyota, but not a Ford.
SELECT name, id
FROM TableA a
JOIN TableB b ON a.id = b.id
WHERE a.id IN (SELECT b.id where Car = 'Toyota') AND
a.id NOT IN (SELECT b.id where Car = 'Ford')
I want to understand why this code does not bring back the ids who have bought a toyota but not a ford, even if they bought multiple cars? What about the logic am I missing?
Thanks in advance.

To start with the why it isn't working, SQL is evaluating the WHERE clause sections in pieces. When it evaluates WHERE a.id IN (SELECT b.id where Car = 'Toyota'), all that remains in the table is records where that ID had Toyota since you are using relative subqueries. So, when it passes to the AND a.id NOT IN (SELECT b.id where Car = 'Ford'), there are no remaining records with Ford in it.
This fiddle should help to illustrate what's happening - https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=085cdad84898f4e498aa9a2e76947d93
The quick fix to this would be to use absolute references to TableB instead of a relative reference in the subquery as shown in this fiddle - https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=f95e3ef0ddab5290ee7b9fa13e7451ab

Related

SQL queries: how to get the value which appears the most in the total of two different tables?

Context: I want to know which vehicle brand appears the most in different accidents.
I have the table vehicle (v_number, brand).
Problem is, I have two different accident tables:
One refers to driven cars involved in an accident, let's call it acc_drive (v_number, acc_number, driver) [v_number FK vehicle]
The other refers to parked cars which are involved in an accident, let's call it acc_park (v_number, acc_number) [v_number FK vehicle, acc_number FK acc_drive]
Now, I'm trying to get the vehicle brand which appears the most in the total of the two tables. For example, if Audi cars appeared 2 times in acc_drive and 3 times in acc_park, the total number of appearences would be 5.
I'm having a really hard time trying to figure this out, so a helping hand would be much appreciated!
UNION ALL can be used to bring the tables together for the JOIN:
select v.brand, count(a.v_number)
from vehicle v left join
((select v_number
from acc_drive
) union all
(select v_number
from acc_park
)
) a
on v.v_number = a.v_number
group by v.brand
order by count(v_number) desc; -- put the biggest numbers first
Note that this uses a left join. So brands with no accidents will be included in the results.
Try this-
SELECT TOP 1 brand,COUNT(*)
FROM vehicle A
INNER JOIN acc_drive B ON A.v_number = B.v_number
INNER JOIN acc_park C ON A.v_number = C.v_number
GROUP BY brand
ORDER BY COUNT(*) DESC

Should I JOIN or should I UNION

I have four different tables I am trying to query on, the first table is where I will be doing most of the querying, but if there is no match in car I am to look in other fields in the other tables to see if there is a match from a VIN parameter.
Example:
Select
c.id,
c.VIN,
c.uniqueNumber,
c.anotheruniqueNumber
FROM Cars c, Boat b
WHERE
c.VIN = #VIN(parameter),
b.SerialNumber = #VIN
Now say that I have no match in Cars, but there is a match in Boat, how would I be able to pull the matching Boat record vs the car record? I have tried to JOIN the tables, but the tables have no unique identifier to reference the other table.
I am trying to figure out what is the best way to search all the tables off of a parameter but with the least amount of code. I thought about doing UNION ALL, but not sure if that what I really want for this situation, seeing as the number of records could get extremely large.
I am currently using SQL Server 2012. Thanks in advance!
UPDATED:
CAR table
ID VIN UniqueIdentifier AnotherUniqueIdentifier
1 2002034434 HH54545445 2016-A23
2 2002035555 TT4242424242 2016-A24
3 1999034534 AGH0000034 2016-A25
BOAT table
ID SerialNumber Miscellaneous
1 32424234243 545454545445
2 65656565656 FF24242424242
3 20023232323 AGH333333333
Expected Result if #VIN parameter matches a Boat identifier:
BOAT
ID SerialNumber Miscellaneous
2 65656565656 FF24242424242
Some sort of union all might be the best approach -- at least the fastest with the right indexes:
Select c.id, c.VIN, c.uniqueNumber, c.anotheruniqueNumber
from Cars c
where c.VIN = #VIN
union all
select b.id, b.VIN, b.uniqueNumber, b.anotheruniqueNumber
from Boats b
where b.VIN = #VIN and
not exists (select 1 from Cars C where c.VIN = #VIN);
This assumes that you have the corresponding columns in each of the tables (which your question implies is true).
The chain of not exists can get longer as you add more entity types. A simple way around is to do sorting instead -- assuming you want only one row:
select top 1 x.*
from (Select c.id, c.VIN, c.uniqueNumber, c.anotheruniqueNumber, 1 as priority
from Cars c
where c.VIN = #VIN
union all
select b.id, b.VIN, b.uniqueNumber, b.anotheruniqueNumber, 2 as priority
from Boats b
where b.VIN = #VIN
) x
order by priority;
There is a slight overhead for the order by. But frankly speaking, ordering 1-4 rows is trivial from a performance perspective.

Returning duplicated values only once from a join query

I'm trying to extract info from a table in my database based on a persons job. In one table i have all the clients info, in another table linked by ID_no their job title and the branches theyre associated with. the problem I'm having is when i join both tables I'm returning some duplicates because a person can be associated with more than one branch.
I would like to know how to return the duplicated values only once, because all I care about for the moment is the persons id number and what their job title is.
SELECT *
FROM dbo.employeeinfo AS ll
LEFT OUTER JOIN employeeJob AS lly
ON ll.id_no = lly.id_no
WHERE lly.job_category = 'cle'
I know Select Distinct will not work in this situation since the duplicated values return different branches.
Any help would be appreciated. Thanks
I'm using sql server 2008 by the way
*edit to show result i would like
------ ll. ll. lly. lly.
rec_ID --employeeID---Name-----JobTitle---Branch------
1 JX100 John cle london
2 JX100 John cle manchester
3 JX690 Matt 89899 london
4 JX760 Steve 12345 london
I would like the second record to not display because i'm not interested in the branch. i just need to know the employee id and his job title, but because of how the tables are structured it's returning JX100 twice because he's recorded as working in 2 different branches
You must use SELECT DISTINCT and specify you ONLY want person id number and job title.
I don't know exactly your fields name, but I think something like this could work.
SELECT DISTINCT ll.id_no AS person_id_number,
lly.job AS person_job
FROM dbo.employeeinfo AS ll LEFT OUTER JOIN
employeeJob AS lly ON ll.id_no = lly.id_no
WHERE lly.job_category = 'cle'

Writing an SQL Query to solve an SQL Puzzle

I need a bit of help/direction as to how to write an SQL query using the following information:
The Question is:
The manager would like to see all uf the cars currently in
the showroom and their price, in the following form: [Make] [Model] –
£[Price] e.g. “VW GOLF - 3000”. Write a SQL query to satisfy this
request.
__
I've broken it down into how I think it needs to work-
First it needs to look at the CarID's in the showroom table and match them with ID in the car table
Then it needs to display the model of the car from the Car table
2,5,8 = fiesta, golf and 307
Once the Name has been found, it checks the ParentCarID to see what the make is
ford, VW and peugeot
The make and model are then displayed alongside the price making the result of the query:
CarID Model Make Price
2 Fiesta Ford 4000
5 Golf VW 3000
8 307 Peugeot 5000
_
After watching and reading a lot about various queries I think that I need to use a relational query or a query with a join, i'd appreciate any help a lot :)
You can get the make and model by joining the table onto itself via the parentID:
SELECT * FROM tableA
JOIN tableB ON tableA.ID = tableB.parentID
Then you just need to join the prices from the showroom table:
SELECT Model.ID, Make.Name, Model.Name, S.Price FROM car Make
JOIN car Model ON Make.id = Model.parentCarID
JOIN showroom S ON Model.ID = S.CarID
SELECT a.MakeCode,
a.ModelCode,
b.Price
FROM CarTable a
INNER JOIN ShowRoom
ON a.ID = b.CarID
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
i hope this query will work has you expected.
select c.Makecode, c.madelcode,s.price from car c, showroom s where c.id=s.carid

SQL mapping between multiple tables

This is a SQL design question. First, the setup. I have three tables:
A, which is automatically populated based on a query against a linked server. The data in this table cannot be changed;
B, which has just a dozen or so rows, containing the names for collections of As;
AtoB, which is the mapping table by which As are organized into named collections, with foreign keys on both columns;
For example, A contains:
Giraffe
Owl
Tiger
And B contains:
Seattle Zoo
San Jose Zoo
And AtoB contains:
1,1 (Giraffe in Seattle)
2,1 (Owl in Seattle)
3,1 (Tiger in Seattle)
2,2 (Owl in San Jose)
Now, the problem:
I've been asked to include in some of these collections items not found in A. So, I create a table, C, with the same identity and Name columns as A, and populate it. In keeping with the earlier example, let's say C contains:
Dragon
The question is, how do I include items from C in AtoB? What if I need to include a Dragon in the Seattle Zoo?
My first instinct, being naive, was to create a view V containing the union of A and C, and modifying AtoB to be VtoB. That's where my naivety paid off: one cannot create a foreign key to a view.
I suspect that there's a standard, correct means of relating one or more A OR C with a B.
To expand on Arthur Thomas's solution here's a union without the WHERE in the subselects so that you can create a universal view:
SELECT A.Name as Animal, B.Name as Zoo FROM A, AtoB, B
WHERE AtoB.A_ID = A.ID && B.ID = AtoB.B_ID
UNION
SELECT C.Name as Animal, B.Name as Zoo FROM C, CtoB, B
WHERE CtoB.C_ID = C.ID && B.ID = CtoB.B_ID
Then, you can perform a query like:
SELECT Animal FROM zoo_animals WHERE Zoo="Seattle Zoo"
If you can't put a Dragon in A then you will need to create another table and another link table. The problem is creating a unique set of data that needs to be stored (another table) that cannot be the same set as A. Since it isn't the same set then you can no longer use the link table (AtoB) which has foreign keys that ensure that the link is a reference from set A. So you could create a tables like this:
imaginary_creatures
id
name
imaginary_creatures_to_b
imaginary_creatures_id (link to imaginary_creatures table)
b_id (link to zoos table)
Later when you want to get all creatures in a zoo you can do a UNION
SELECT A.Name FROM A where A.ID IN
(SELECT AB.A_ID FROM AtoB AB WHERE B_ID =
(SELECT B.ID FROM B WHERE B.Name = 'Zoo Name'))
UNION
SELECT i.name FROM imaginary_creatures i i.id IN
(SELECT ic.imaginary_creatures_id FROM imaginary_creatures_to_c ic
WHERE ic.b_id = (SELECT B.ID FROM B WHERE B.Name = 'Zoo Name'))
There may be a better way of writing that, but it should work for your purposes.
Arthur Thomas has a good solution, the other possible solution is to add a column to the link table indicating which table (A or C) it is related to. Then enforce the relationships through triggers rather than foreign keys. But really Arthur's solution is the preferred way of doing this sort of thing.
What you want to do is put Dragon in A, and if you want to select ALL records from A regardless of if they have a matching record in AtoB, do a LEFT OUTER JOIN. Something like this:
SELECT * FROM A
LEFT OUTER JOIN AtoB
ON A.id = AtoB.A_ID
Edit: This would only work if you could add your new records to A. I missed the fact that you are not able to. I think Arthur Thomas's solution is what you want.
Truncate the table dept_details
Display the structure of the table emp_details
Convert the first letter of emp_name into capitals.