Access SQL JOIN with subquery - sql

Can someone tell me how I can convert this query to work in MS Access?
SELECT Shooters.idShooters, Shooters.firstname, Shooters.lastname, JoinedContingent.Count, JoinedShots.Count
FROM Shooters
INNER JOIN
(SELECT Shooters.idShooters, Count(Contingent.idContingent) AS Count
FROM Shooters LEFT JOIN Contingent ON Shooters.idShooters = Contingent.fidShooter
GROUP BY Shooters.idShooters)
AS JoinedContingent ON JoinedContingent.idShooters = Shooters.idShooters
INNER JOIN
(SELECT Shooters.idShooters, Count(Shots.idShots) AS Count
FROM Shooters LEFT JOIN Shots ON Shooters.idShooters = Shots.fidShooters
GROUP BY Shooters.idShooters)
AS JoinedShots ON JoinedShots.idShooters = Shooters.idShooters;
Background information:
I would like to count the foreign key occurrences for each Shooter in the table Contingent and Shots. The result should look like this:
idShooters | firstname | lastname | Count | Count
____________________________________________________________
1 John Doe 0 10
2 Jane Doe 1 20
.
.
.

I think this is what you want:
SELECT s.idShooters, s.firstname, s.lastname,
NZ(c.Count, 0), NZ(sh.Count, 0)
FROM (Shooters as s LEFT JOIN
(SELECT c.fidShooter, Count(*) AS Count
FROM Contingent as c
GROUP BY c.fidShooter
) as c
ON s.idShooters = c.fidShooter
) LEFT JOIN
(SELECT sh.fidShooters, Count(*) AS Count
FROM Shots as sh
GROUP BY sh.fidShooters
) as sh
ON s.idShooters = sh.fidShooters;
Note that I moved the outer join to the outer query. Actually, no joins are needed in the subqueries, so don't bother.

Related

SQL query to retrieve average of different aggregation type in single query

This is my data model:
I need to get name, surname and personal votes average of all students in classroom that have the average lower then the votes average of all students in classroom. Following the desired output:
+---------+------+--------+-------------+---------------+
| surname | name | class | class_avg | studend_avg |
+---------+------+--------+-------------+---------------+
| b | b | 1 | 4.1250 | 2.7500 |
+---------+------+--------+-------------+---------------+
I wrote following query that works correctly in mariadb database:
SELECT student.surname, student.name, student.classroom, classroom.average AS classroom_average, AVG(vote.vote) AS student_average
FROM (student INNER JOIN vote ON student.fiscalcode = vote.fiscalcode)
INNER JOIN
(select student.classroom AS classroom, AVG(vote.vote) AS average
FROM student INNER JOIN vote ON student.fiscalcode = vote.fiscalcode
GROUP BY student.classroom) AS classroom
ON student.classroom=classroom.classroom
GROUP BY student.surname, student.name, student.classroom, classroom.average
HAVING AVG(vote.vote) < classroom.average;
But I have this error in MS Access:
Your Query does not include the specified expression "AVG(vote.vote)/AVG(vote.vote) < classroom.average" as part of an aggregate function
There are some other easier method to write this query?
Consider:
SELECT StudentAvg.fiscalcode, student.name, student.surname, StudentAvg.AvgOfvote, ClassAvg.AvgOfvote
FROM ((SELECT student.classroom, student.fiscalcode, Avg(vote.vote) AS AvgOfvote
FROM student INNER JOIN vote ON student.fiscalcode = vote.fiscalcode
GROUP BY student.classroom, student.fiscalcode) As StudentAvg INNER JOIN (SELECT student.classroom, Avg(vote.vote) AS AvgOfvote
FROM vote INNER JOIN student ON vote.fiscalcode = student.fiscalcode
GROUP BY student.classroom) AS ClassAvg ON StudentAvg.classroom = ClassAvg.classroom) INNER JOIN student ON StudentAvg.fiscalcode = student.fiscalcode
WHERE (((StudentAvg.AvgOfvote)<[ClassAvg]![AvgOfvote]));

postgres STRING_AGG() returns duplicates?

I have seen some similar posts, requesting advice for getting distinct results from the query. This can be solved with a subquery, but the column I am aggregating image_name is unique image_name VARCHAR(40) NOT NULL UNIQUE. I don't believe that should be necersarry.
This is the data in the spot_images table
spotdk=# select * from spot_images;
id | user_id | spot_id | image_name
----+---------+---------+--------------------------------------
1 | 1 | 1 | 81198013-e8f8-4baa-aece-6fbda15a0498
2 | 1 | 1 | 21b78e4e-f2e4-4d66-961f-83e5c28d69c5
3 | 1 | 1 | 59834585-8c49-4cdf-95e4-38c437acb3c1
4 | 1 | 1 | 0a42c962-2445-4b3b-97a6-325d344fda4a
(4 rows)
SELECT Round(Avg(ratings.rating), 2) AS rating,
spots.*,
String_agg(spot_images.image_name, ',') AS imageNames
FROM spots
FULL OUTER JOIN ratings
ON ratings.spot_id = spots.id
INNER JOIN spot_images
ON spot_images.spot_id = spots.id
WHERE spots.id = 1
GROUP BY spots.id;
This is the result of the images row:
81198013-e8f8-4baa-aece-6fbda15a0498,
21b78e4e-f2e4-4d66-961f-83e5c28d69c5,
59834585-8c49-4cdf-95e4-38c437acb3c1,
0a42c962-2445-4b3b-97a6-325d344fda4a,
81198013-e8f8-4baa-aece-6fbda15a0498,
21b78e4e-f2e4-4d66-961f-83e5c28d69c5,
59834585-8c49-4cdf-95e4-38c437acb3c1,
0a42c962-2445-4b3b-97a6-325d344fda4a,
81198013-e8f8-4baa-aece-6fbda15a0498,
21b78e4e-f2e4-4d66-961f-83e5c28d69c5,
59834585-8c49-4cdf-95e4-38c437acb3c1,
0a42c962-2445-4b3b-97a6-325d344fda4a
Not with linebreaks, I added them for visibility.
What should I do to retrieve the image_name's one time each?
If you don't want duplicates, use DISTINCT:
String_agg(distinct spot_images.image_name, ',') AS imageNames
Likely, there are several rows in ratings that match the given spot, and several rows in spot_images that match the given sport as well. As a results, rows are getting duplicated.
One option to avoid that is to aggregate in subqueries:
SELECT r.avg_raging
s.*,
si.image_names
FROM spots s
FULL OUTER JOIN (
SELECT spot_id, Round(Avg(ratings.rating), 2) avg_rating
FROM ratings
GROUP BY spot_id
) r ON r.spot_id = s.id
INNER JOIN (
SELECT spot_id, string_agg(spot_images.image_name, ',') image_names
FROM spot_images
GROUP BY spot_id
) si ON si.spot_id = s.id
WHERE s.id = 1
This actually could be more efficient that outer aggregation.
Note: it is hard to tell without seeing your data, but I am unsure that you really need a FULL JOIN here. A LEFT JOIN might actually be what you want.

Is it possible to replace content from multiple foreign keys when doing a query?

I have the following tables:
TABLE PLAYER
id | name
1 | A
2 | B
3 | C
4 | D
TABLE PAIRINGS
id | player_a | player_b
1 | 3 |1
2 | 2 |4
Both columns in table Pairings are foreign keys to column id of table player.
My problem is, I would like to avoid making a query from code every time I want to know which is my player's name (like, Select name From Player Where Id = pairings.player_a). I have thought about adding Name as an extra columnd to Pairings table, but that would denormalize it.
Instead, it would be nice if I could get the names in just one query, like:
RESULT
player_a | player_b
C | A
B | D
Is it this possible? Thanks so much in advance.
You may join the PAIRINGS table to the PLAYER table, twice:
SELECT
p1.name AS player_a,
p2.name AS player_b
FROM PAIRINGS pr
INNER JOIN PLAYER p1
ON pr.player_a = p1.id
INNER JOIN PLAYER p2
ON pr.player_b = p2.id;
Demo
Don't do it! One of the points of using a relational database is that data is stored in only one place. That is a big convenience. Of course, there are exceptions, but these exceptions should have firm reasons.
In this case, just define a view:
CREATE VIEW vw_pairings AS
SELECT p.*, pa.name AS player_a_name,
pb.name AS player_b_name
FROM PAIRINGS p JOIN
PLAYER pa
ON p.player_a = pa.id JOIN
PLAYER pb
ON p.player_b = pb.id;
When you query from the view, you will see the names, along with all the other information in the PAIRINGS table.
Hope can help you
Select * Into #PLAYER From (
Select 1 [ID], 'A' [Name] Union All
Select 2 [ID], 'B' [Name] Union All
Select 3 [ID], 'C' [Name] Union All
Select 4 [ID], 'D' [Name]
) A
Select * Into #PAIRINGS From (
Select 1 [ID], 3 [PLAYER_A], 1 [PLAYER_B] Union All
Select 2 [ID], 2 [PLAYER_A], 4 [PLAYER_B]
) A
Select
P.ID, A.NAME, B.NAME
From #PAIRINGS P
Left Join #PLAYER A On A.ID = P.PLAYER_A
Left Join #PLAYER B On B.ID = P.PLAYER_B
You can create view, for avoid making query
Example
Create View vwPAIRINGS As
Select
P.ID, A.NAME, B.NAME
From #PAIRINGS P
Left Join #PLAYER A On A.ID = P.PLAYER_A
Left Join #PLAYER B On B.ID = P.PLAYER_B
After that, just select usual
Select * from vwPAIRINGS

Query sql to get the first occurrence in a many to many relationship

I have a User table that has a many to many relationship with Areas. This relationship is stored in the Rel_User_area table. I want to show the user name and the first area that appears in the list of areas.
Ex.
User
id | Name
1 | Peter
2 | Joe
Area
id | Name
1 | Area A
2 | Area B
3 | Area C
Rel_User_area
iduser | idarea
1 | 1
1 | 3
2 | 3
The result I want:
User Name | Area
Peter |Area A
Joe |Area C
Using the minimum area id to determine "First" you could use a correlated subquery (A subquery that refers to field(s) in the main query to filter results):
SELECT user.name, area.name
FROM
user
INNER JOIN Rel_User_Area RUA ON user.id = RUA.iduser
INNER JOIN Area ON RUA.idarea = area.id
WHERE area.id = (SELECT min(idarea) FROM Rel_User_Area WHERE iduser = RUA.iduser)
There's other ways of doing this that may be RDBMS specific. Like in Teradata I would use a QUALIFY clause that doesn't exist in MySQL, SQL Server, Oracle, Postgres, etc.. Regardless of the RDBMS the above should work.
SELECT user.name, area.name
FROM
user
INNER JOIN Rel_User_Area RUA ON user.id = RUA.iduser
INNER JOIN Area ON RUA.idarea = area.id
QUALIFY ROW_NUMBER() OVER (PARTITION BY user.id ORDER BY area.id ASC) = 1;
using the ID from Rel_user_Area you mentioned in comments...
This should be pretty platform independent.
SELECT U.name as Username, A.Name as Area
FROM (SELECT min(ID) minID, IDUser, IDarea
FROM Rel_user_Area
GROUP BY IDUser, IDarea) UA
INNER JOIN User U
on U.ID = UA.IDuser
INNER JOIN Area A
on A.ID = UA.IDArea
If Cross apply and top work (could substitute limit 1 vs top if Postgresql or mySQL)
This will run the cross apply SQL once for each record in user; thus you get the most recent rel_user_Area ID per user.
SELECT U.name as Username, A.Name as Area
FROM User U
on U.ID = UA.IDuser
CROSS APPLY (SELECT TOP 1 IDUser, IDArea
FROM Rel_user_Area z
WHERE Z.IDUSER = U.ID
ORDER BY ID ASC) UA
INNER JOIN Area A
on A.ID = UA.IDArea

how use distinct in second join table in sql server

I have a SQL table consists of id, name, email,.... I have another SQL table that has id, email, emailstatus but these 2 id are different they are not related. The only thing that is common between these 2 tables are emails.
I would like to join these 2 tables bring all the info from table1 and if the email address from table 1 and table 2 are same and emailstatus is 'Bounced'. But the query that I am writing gives me more record than I expected because there are multiple rows in tbl_webhook(second table) for each row in Applicant(first table) .I want to know if applicant has EVER had an email bounce.
Query without join shows 23000 record but after join shows 42000 record that is because of duplicate how I can keep same 23000 record only add info from second table?
This is my query:
SELECT
A.[Id]
,A.[Application]
,A.[Loan]
,A.[Firstname]
,A.[Lastname]
,A.[Email],
,H.[Email], H.[EmailStatus] as BouncedEmail
FROM Applicant A (NOLOCK)
left outer join [tbl_Webhook] [H] (NOLOCK)
on A.Email = H.Email
and H.[event]='bounced'
this is sample of desired data:
id email name emailFromTable2 emailstatus
1 test2#yahoo.com lili test2#yahoo.com bounced
2 tesere#yahoo.com mike Null Null
3 tedfd2#yahoo.com nik tedfd2#yahoo.com bounced
4 tdfdft2#yahoo.com sam Null Null
5 tedft2#yahoo.com james tedft2#yahoo.com bounced
6 tedft2#yahoo.com San Null
Use a nested select for this type of query. I would write this as:
select id, application, load, firstname, lastname, email,
(case when BouncedEmail is not null then email end) as EmailFromTable2,
BouncedEmail
from (SELECT A.[Id], A.[Application], A.[Loan], A.[Firstname], A.[Lastname], A.[Email],
(case when exists (select 1
from tbl_WebHook h
where A.Email = H.Email and H.[event] = 'bounced'
)
then 'bounced
end) as BouncedEmail
FROM Applicant A (NOLOCK)
) a
You can also do this with cross apply, but because you only really need one column, a correlated subquery also works.
;WITH DistinctEmails
AS
(
SELECT * , rn = ROW_NUMBER() OVER (PARTITION BY [Email] ORDER BY [Email])
FROM [tbl_Webhook]
)
SELECT
A.[Id]
,A.[Application]
,A.[Loan]
,A.[Firstname]
,A.[Lastname]
,A.[Email],
,H.[Email], H.[EmailStatus] as BouncedEmail
FROM Applicant A (NOLOCK) left outer join DistinctEmails [H] (NOLOCK)
on A.Email = H.Email
WHERE H.rn = 1
and H.[event]='bounced'
i believe query below should be enough to select distinct bounced email for you, cheer :)
SELECT
A.[Id]
,A.[Application]
,A.[Loan]
,A.[Firstname]
,A.[Lastname]
,A.[Email],
,H.[Email], H.[EmailStatus] as BouncedEmail
FROM Applicant A (NOLOCK)
Inner join [tbl_Webhook] [H] (NOLOCK)
on A.Email = H.Email
and H.[EmailStatus]='bounced'
basically i just change the joining to inner join and change the 2nd table condition from event to emailstatus, if u can provide your table structure and sample data i believe i can help you up :)