Multiple SQL jointure - sql

I will try to be the most intelligible possible as I'm a very beginner in SQL.
Here is my issue, I have a database with answers from questionnaries.
Each database have a user.id, unique.id, date, final score. And I would like to extract those data with a simple table which group the score of each questionnary by user.id and by date.
I have tried this :
SELECT A.user_id, SUBSTRING(A.created_at,1,10), A.SCORE, B.SCORE,
C.SCORE, D.SCORE, E.SCORE
FROM A
LEFT JOIN B
ON A.user_id = A.user_id AND SUBSTRING(A.created_at,1,10) = SUBSTRING(B.created_at,1,10)
LEFT JOIN C
ON A.user_id = C.user_id AND SUBSTRING(A.created_at,1,10) = SUBSTRING(C.created_at,1,10)
LEFT JOIN D
ON A.user_id = D.user_id AND SUBSTRING(A.created_at,1,10) = SUBSTRING(D.created_at,1,10)
LEFT JOIN E
ON A.user_id = E.user_id AND SUBSTRING(A.created_at,1,10) = SUBSTRING(E.created_at,1,10)
This is almost successful but I find out if some one did not participate to the A questionnary then I don't have anything about it.
I hope, I have been clear enough.
Thank you all,

You must manage your query with USER table (if you have it), as follow:
SELECT U.user_id, SUBSTRING(A.created_at,1,10), A.SCORE, B.SCORE,
C.SCORE, D.SCORE, E.SCORE
FROM USER U
LEFT JOIN A
ON U.user_id = A.user_id AND SUBSTRING(U.created_at,1,10) = SUBSTRING(A.created_at,1,10)
LEFT JOIN B
ON U.user_id = B.user_id AND SUBSTRING(U.created_at,1,10) = SUBSTRING(B.created_at,1,10)
LEFT JOIN C
ON U.user_id = C.user_id AND SUBSTRING(U.created_at,1,10) = SUBSTRING(C.created_at,1,10)
LEFT JOIN D
ON U.user_id = D.user_id AND SUBSTRING(U.created_at,1,10) = SUBSTRING(D.created_at,1,10)
LEFT JOIN E
ON U.user_id = E.user_id AND SUBSTRING(U.created_at,1,10) = SUBSTRING(E.created_at,1,10)

I would suggest two things, depending on how restricted you are to modify your tables.
Just use one table. Add a column testType or something like this. You can ever foreign key it and use only the keys there. That would make the recordset smaller.
Then put all your results in that one table with the matching keys.
Use UNION if the tables are all the same and have just different entries, then query this union.
There is no need for a JOIN in your case, it's better to adapt the structure.
Otherwise you can try FULL JOIN to keep all the data.

Related

SQL query optimization practice

I want to pratice my skills in SQL query optimization for small databases.
I have created a very simple database:
I populated it with hunderds of thousands of entries and need some complex, stupid and unoptimized queries to make some tests and practice my optimization skills. Right now I tried to write some queries but they don't make much impact on the database.
Which key words should I use? Could someone help?
I find it hard to come up with queries that are really bad :-) Here are some ideas. Hopefully, others will come up with more. I suggest you put your tables (empty or even with some sample rows) in https://dbfiddle.uk/, where we can try our queries. I don't know for instance, if I don't have any syntax errors in below queries.
Find all videos that are either owned or commented by a user named xyz:
select distinct v.*
from videos v
left join user_have_videos uv on uv.video_id = v.video_id
left join users u1 on u1.user_id = uv.user_id
left join comments c on c.video_id = v.video_id
left join users u2 on u2.user_id = c.user_id
where u1.user_name = :user_name
or u2.user_name = :user_name;
Find all users that own at least three different videos:
select distinct u.*
from users u
left join user_have_videos uv1 on uv1.user_id = u.user_id
left join videos v1 on v1.video_id = uv1.video_id
left join user_have_videos uv2 on uv2.user_id = u.user_id
left join videos v2 on v2.video_id = uv2.video_id
left join user_have_videos uv3 on uv3.user_id = u.user_id
left join videos v3 on v3.video_id = uv3.video_id
where v1.video_id <> v2.video_id
and v1.video_id <> v3.video_id
and v2.video_id <> v1.video_id
and v2.video_id <> v3.video_id
and v3.video_id <> v1.video_id
and v3.video_id <> v2.video_id;
Find all users that commented on John Wayne:
select distinct u.*
from users u
join comments c on c.user_id = u.user_id
where lower(comments) like '%john wayne%';
And here is one where you shall find out what this does and make it better:
with cte(user_id, video_id, num) as
(
select u.user_id, v.video_id, 1
from videos v
join user_have_videos uv on uv.video_id = v.video_id
join users u on u.user_id = uv.user_id
union all
select u.user_id, min(v.video_id), min(cte.num) + 1
from videos v
join user_have_videos uv on uv.video_id = v.video_id
join users u on u.user_id = uv.user_id
join cte on cte.user_id = u.user_id
and cte.video_id < v.video_id
group by u.user_id
)
select distinct u.*
from users u
join cte on cte.user_id = u.user_id and cte.num >= 10;

transform union query to join query

I have a simple database schema composed of 3 tables
User
id
name
matricule
Document
id
serial
user_id(owner of document : foreign key to User(id))
User_Document (join table)
user_id
document_id
I want all document serial from both user sources : owner of document and join table . The query is filtered by a list of users matricule
I managed to achieve the desired goal with union query :
select d.serial from Document d
INNER JOIN users u ON u.id = d.user_id
where u.matricule IN ('1234')
UNION
select d.serial from Document d
inner join User_Document ud on d.id = ud.document_id
inner join users u on ud.user_id = u.id
where u.matricule IN ('1234')
How to arrive to the same result with only a join query ? I need as well skip document with no serial ( this case is possible)
Thank you very much
Try this:
SELECT d.serial
FROM Document d
LEFT JOIN User_Document ud
ON d.id = ud.document_id
LEFT JOIN users u
on ud.user_id = u.id
LEFT JOIN users u2
ON d.user_id = u2.id
WHERE d.serial IS NOT NULL
AND
(
ISNULL(u.matricule,'') IN ('1234')
OR ISNULL(u2.matricule,'') IN ('1234')
)
Why do you want to remove the joins? Probably the most efficient query would use exists:
select d.*
from documents d
where d.serial is not null and
(exists (select 1
from users u
where u.id = d.user_id and u.matricule = '1234'
) or
exists (select 1
from user_document ud join
users u
on u.id = d.user_id
where ud.document_id = d.id and u.matricule = '1234'
)
);
For this, you would then want indexes on users(id, matricule), user_documents(document_id, user_id), and documents(serial, user_id, document_id).
The use of indexes saves the elimination of duplicates -- which should be a big win for this type of query.

Postgres hangs when a possible null column value is used in SELECT statement

I'm trying to mash some user data from several tables together in Postgres in order to display information in a table on a web page. Users may or may not have "contact" information, and I'm trying to include the "contact" email address as part of what gets returned in the select query.
The query I'm using boils down to the following:
select u.user_id, r.role_id, u.first_name, u.last_name, c.email, o.state_active, ce.name
from user
join user_role r on u.user_id = r.user_id
join company c on r.company_id = c.company_id
join object_state o on u.state_id = o.state_id
left outer join user_contact uc on u.user_id = uc.user_id and uc.default_flag = 'Y' and uc.status = 'A'
left outer join contact c on uc.contact_id = c.contact_id and c.contact_type like 'E%'
I keep running into problems around the outer joined tables at the bottom. It appears that if I do a SELECT * sort of query, I'll get back everything I expect, but as soon as I run the above query, the statement just hangs and never returns values. If, however, I remove the c.email portion of the select statement, everything returns quickly with no problems at all.
Am I missing some sort of core feature of Postgres where I'm unable to select a column that might be null, or is there something else obvious that I'm completely missing as far as this query goes? I don't understand why it works fine if I don't require the email, but not otherwise.
Could you provide a sqlfiddle with a sample of your database to test our solutions please ?
You can try this :
select u.user_id, r.role_id, u.first_name, u.last_name, COALESCE(c.email,NULL), o.state_active, ce.name
from user
join user_role r on u.user_id = r.user_id
join company c on r.company_id = c.company_id
join object_state o on u.state_id = o.state_id
left outer join user_contact uc on u.user_id = uc.user_id and uc.default_flag = 'Y' and uc.status = 'A'
left outer join contact c on COALESCE(uc.contact_id,-1) = c.contact_id and c.contact_type like 'E%'
Here, you should look at the COALESCE.

sql select query among 3 tables

I have three tables:
ITEMDISPLAYCOuNTS *this table stores who displayed which posts and howmanytimes
postid, count, whodisplayedid
POSTS *this table stores who posted what?
postid, whopostedid
ASPNET_USERS
userid, username
What I want at the end is who displayed whose post, and how many times, with usernames, not userids:
OUTPUT
UserNameWhoDisplayed, UserNameWhosePost, Count
I wrote the following code, but it is not functioning properly.
SELECT u1.UserName, u2.UserName, ItemDisplayCounts.Count
FROM ItemDisplayCounts AS i, Posts AS p, aspnet_Users AS u1, aspnet_Users AS u2
WHERE p.UserId = u2.UserId AND i.UserId = u1.UserId AND i.PostId = p.PostId
Can anyone suggest any corrections?
I think you want something like this (changed your joins to proper ANSI ones):
select
iu.UserName as UserNameWhoDisplayed,
pu.UserName as UserNameWhosePost,
sum(i.Count) as [Count]
from ItemDisplayCounts as i
inner join aspnet_Users as iu on iu.userid = i.whodisplayedid
inner join posts as p on p.postid = i.postid
inner join aspnet_Users as pu on pu.userid = p.whopostedid
group by
iu.UserName,
pu.UserName
If I am understanding you correctly you do not need to perform any math in the query, since you have the count field in the ITEMDISPLAYCOuNTS table. So, I do believe this will work for you:
SELECT U2.username AS UserNameWhoDisplayed,
U1.username AS UserNameWhosePost,
ID.[Count]
FROM ((POSTS AS P
INNER JOIN ASPNET_USERS AS U1 ON P.whopostedid = U1.userid)
INNER JOIN ITEMDISPLAYCOuNTS AS ID ON P.postid = ID.postid)
INNER JOIN ASPNET_USERS AS U2 ON ID.whodisplayedid = U2.userid

SQL join help for friend list

I have three database tables: users, user_profiles and friends:
users
id
username
password
user_profiles
id
user_id
full_name
friends
id
usera_id
userb_id
What would be a query which finds the friends list of any user and also joins the table users and user_profiles to get the profile and user information of that friend?
Use:
SELECT f.username,
up.*
FROM USERS f
JOIN USER_PROFILES up ON up.user_id = f.id
JOIN FRIENDS fr ON fr.userb_id = f.id
JOIN USERS u ON u.id = fr.usera_id
WHERE u.username = ?
...assuming userb_id is the friend id.
This may not be the best way to do it, but this felt like the logical way:
select a.id , a.friend_id ,
Users.username
from
( SELECT id , IF(usera_id = 1, userb_id , usera_id) friend_id
FROM friends
where usera_id = 1 OR userb_id = 1 ) a
left join Users on a.friend_id = Users.id
this uses a mySQL function so probably wont work in Oracle/MSSQL
Falling back on bad habits (not using JOIN notation in the FROM clause):
SELECT a.id, a.username, a.full_name,
b.id, b.username, b.full_name
FROM friends AS f, users AS ua, users AS ub,
user_profiles AS a, user_profiles AS b
WHERE f.usera_id = ua.id
AND f.userb_id = ub.id
AND a.user_id = ua.id
AND b.user_id = ub.id
The key point is using table aliases (all those 'AS' clauses) and referencing the same table more than once when necessary.
Someone could write this with JOIN instead.
Some modification to eugene y's answer, will this work?
SELECT * FROM users u
JOIN friends f ON (f.userb_id = u.id OR f.usera_id = u.id)
JOIN user_profiles p ON u.id = p.user_id
WHERE u.id = ?