Getting data from 2 or more tables: select or join? - sql

Let's say there are 2 or more tables.
Table A: aID, name, birthday
Table B: bID, petType, petName
Table C: cID, stackOverFlowUsername
I want to get something like aID, name, birthday, number of cats a person has, stack overflow's username
We can
use joins to join all 3 tables select * from tableA... tableB... tableC...
use multiple select statements, select a.*, (select count(*) from tableB where petType = 'cat') as numberOfCats, (select...) as stackUsername from tableA a
or other ways that I didn't know
My question is when is the right situation to use select, joins or is there even better methods?
Update:
Here is another question. If I have 3 stackoverflow accounts, Tom has 1 and Peter has 2,
using
A left join B left join C
will return a total of 6 rows
select a.*, select count(*) from tableB where..., select top 1 stackOverFlowUsername from tableC
returns 3 rows because there are 3 person
Can I use joins to achieve something similar if I only want one row of data for each person in tableA regardless how many stackoverflow accounts he/she has?
Thanks

A selected sub-select (case 2) might be scanned for every result row, while joined tables/views/subselects are calculates only once: saving memory and joining time (with pre-built indices). Once you are used to talking SQL, you will find that the JOIN syntax is many times easier to read.

Related

How do I find the rows of two tables where a row element from TableA is different from TableB

I am using SQL Server.
I am trying to find the groups that a student is not part of. Table A is a list of all the groups in the database. Table B shows the groups that each student is apart of. When I try joining the two tables and then using WHERE to see the differences, it does not work. I think I have to use EXCEPT but I have no idea how to.
The goal
What I want to happen is have a query, for example studentA, and then it must return all the groups that studentA is NOT apart of. Therefore, for the example tables below it should return groupC because it is the only group that studentA is NOT apart of.
TableA:
groupName
groupDescritpion
groupTags
group A
doesnt matter
doesnt matter
group B
doesnt matter
doesnt matter
group C
doesnt matter
doesnt matter
TableB:
username
groupName
studentA
group A
studentA
group B
studentB
group B
studentB
group C
What I tried:
SELECT DISTINCT TableA.groupName, TableA.groupDescription
FROM TableA
INNER JOIN TableB
ON TableA.groupName=TableB.groupName
WHERE TableB.username != 'studentA';
What should happen:
username
groupName
studentA
group C
SELECT DISTINCT T.USERNAME,A.groupName
FROM TABLEB AS T
CROSS JOIN TABLEA AS A
EXCEPT
SELECT X.USERNAME,X.GROUPNAME
FROM TABLEB AS X

SQLite - How to select records from one table that are not in another table

I have a database with 3 tables; tblCustomers, tblBookings, tblFlights.
I want to find the customer's last name (LName), from the Customers table where the customers do not appear in the bookings table. It should return just three names, but it returns the names 10 times each. There are 10 records in the bookings table, so I think the command is returning the correct names, but not once...
I have tried:
SELECT tblCustomers.LName
FROM tblCustomers, tblBookings
WHERE tblCustomers.CustID
NOT IN (SELECT CustID FROM tblBookings)
How do I return just one instance of the name, not the name repeated 10 times?
You are doing a CROSS JOIN of the 2 tables.
Use only NOT IN:
SELECT LName
FROM tblCustomers
WHERE CustID NOT IN (SELECT CustID FROM tblBookings)
The (implicit) cross join on The bookings table in the outer query makes no sense - and it multiplies the customer rows.
Also, I would recommend not exists for filtering instead of not in: it usually performs better - with the right index in place, and it is null-safe:
SELECT c.LName
FROM tblCustomers c
WHERE NOT EXISTS (SELECT 1 FROM tblBookings b WHERE b.CustID = c.CustID)
For performance, make sure to have an index on tblBookings(CustID) - if you have a proper foreign key declared, it should already be there.

Self Join bringing too many records

I have this query to express a set of business rules.
To get the information I need, I tried joining the table on itself but that brings back many more records than are actually in the table. Below is the query I've tried. What am I doing wrong?
SELECT DISTINCT a.rep_id, a.rep_name, count(*) AS 'Single Practitioner'
FROM [SE_Violation_Detection] a inner join [SE_Violation_Detection] b
ON a.rep_id = b.rep_id and a.hcp_cid = b.hcp_cid
group by a.rep_id, a.rep_name
having count(*) >= 2
You can accomplish this with the having clause:
select a, b, count(*) c
from etc
group by a, b
having count(*) >= some number
I figured out a simpler way to get the information I need for one of the queries. The one above is still wrong.
--Rep violation for different HCP more than 5 times
select distinct rep_id,rep_name,count(distinct hcp_cid)
AS 'Multiple Practitioners'
from dbo.SE_Violation_Detection
group by rep_id,rep_name
having count(distinct hcp_cid)>4
order by count(distinct hcp_cid)

SQL, only if matching all foreign key values to return the record?

I have two tables
Table A
type_uid, allowed_type_uid
9,1
9,2
9,4
1,1
1,2
24,1
25,3
Table B
type_uid
1
2
From table A I need to return
9
1
Using a WHERE IN clause I can return
9
1
24
SELECT
TableA.type_uid
FROM
TableA
INNER JOIN
TableB
ON TableA.allowed_type_uid = TableB.type_uid
GROUP BY
TableA.type_uid
HAVING
COUNT(distinct TableB.type_uid) = (SELECT COUNT(distinct type_uid) FROM TableB)
Join the two tables togeter, so that you only have the records matching the types you are interested in.
Group the result set by TableA.type_uid.
Check that each group has the same number of allowed_type_uid values as exist in TableB.type_uid.
distinct is required only if there can be duplicate records in either table. If both tables are know to only have unique values, the distinct can be removed.
It should also be noted that as TableA grows in size, this type of query will quickly degrade in performance. This is because indexes are not actually much help here.
It can still be a useful structure, but not one where I'd recommend running the queries in real-time. Rather use it to create another persisted/cached result set, and use this only to refresh those results as/when needed.
Or a slightly cheaper version (resource wise):
SELECT
Data.type_uid
FROM
A AS Data
CROSS JOIN
B
LEFT JOIN
A
ON Data.type_uid = A.type_uid AND B.type_uid = A.allowed_type_uid
GROUP BY
Data.type_uid
HAVING
MIN(ISNULL(A.allowed_type_uid,-999)) != -999
Your explanation is not very clear. I think you want to get those type_uid's from table A where for all records in table B there is a matching A.Allowed_type_uid.
SELECT T2.type_uid
FROM (SELECT COUNT(*) as AllAllowedTypes FROM #B) as T1,
(SELECT #A.type_uid, COUNT(*) as AllowedTypes
FROM #A
INNER JOIN #B ON
#A.allowed_type_uid = #B.type_uid
GROUP BY #A.type_uid
) as T2
WHERE T1.AllAllowedTypes = T2.AllowedTypes
(Dems, you were faster than me :) )

SQL Server Return 1 Row Per Boat

Basically, what I want to do is join 4 tables together and return 1 row for each boat.
Table Layouts
[Boats]
id, date, section, raft
[Photos]
id, boatid, pthurl, purl
[River_Company]
id, sort, company, company_short
[River_Section]
id, section
Its very simple as far as structure, however, I've having the time of my life trying to get it to return only 1 row. No boat will ever be on the same day, the only thing that's messing this up is the photo table.
If you know a better way for it to return the record table for all the boats boats and only 1 photo from the photo table, please, please post it!!
Desired Format
boats.id, boats.date, river_company.company, river_section.section, photos.purl, photos.pthurl
It's basically how joins work. Since boats and photos are in one-to-many relationships and you want one-to-one-like query, you need to explicitly express it with predicate. For example:
select b.*
from
boats b
inner join photos p
on b.id = p.boatid
where p.id = (select max(id) from photos where boatid = b.id)
Assuming your ID column is the relation that you have designed:
SELECT Boats.* FROM Boats
LEFT OUTER JOIN Photos on Photos.ID =
(
SELECT TOP 1 Photos.ID FROM Photos
INNER JOIN Boats ON Photos.BoatID = Boats.ID
)
INNER JOIN River_Company on River_Company.ID = Boats.ID
INNER JOIN River_Section on River_Section.ID = Boats.ID
So basically, this will:
Guarantee the maximum row count of 1. (It's a bit dirty, but fact is if you have more than one photo, more than one link will be returned otherwise)
If there are no photo's, the boat will still be returned