transform union query to join query - sql

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.

Related

Multiple SQL jointure

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.

How do I find out which users with a specific RoleID that's not been active within a time interval?

This query down below will tell me how many non-active users there's been during a timeframe.
USE Database
SELECT u.*
FROM [dbo].[tbl_Users] u
WHERE NOT EXISTS (SELECT 1
FROM [dbo].[CaseTable] ct
WHERE c.tUserID = u.UserID AND ct.CreationDate between '2019-01-01' and '2019-12-31'
);
And this query below will tell me the users that have the specific role id I'm looking for.
Use Database;
SELECT UserID, DepartmentID, RoleId
FROM tbl_UsersBelongsTo
WHERE RoleID=6
How can I integrate both queries and essentially get what I'm looking for? I presume it's with a JOIN clause but how??
I think you just want join or additional exists:
SELECT u.*
FROM [dbo].[tbl_Users] u
WHERE NOT EXISTS (SELECT 1
FROM [dbo].[CaseTable] ct
WHERE ct.tUserID = u.UserID AND
ct.CreationDate between '2019-01-01' and '2019-12-31'
) AND
EXISTS (SELECT 1
FROM tbl_UsersBelongsTo ubt
WHERE ubt.RoleID = 6 AND ubt.userId = u.userId
);
Please try to use an inner join like below:
SELECT u.*
FROM [dbo].[tbl_Users] u
INNER JOIN
(
SELECT UserID
FROM tbl_UsersBelongsTo
WHERE RoleID=6
) x ON u.UserID = x.UserID
WHERE NOT EXISTS (SELECT 1
FROM [dbo].[CaseTable] ct
WHERE c.tUserID = u.UserID AND ct.CreationDate between '2019-01-01' and '2019-12-31'
);
You can read more about JOINS here.
If I understood the question correctly - you are using 2 different databases and the name of the 2nd database is pisacara. It is possible to join tables from different databases in SQL Server as long as as those databases are on the same server and you use the same credentials for both databases.
Assuming that tbl_Users table has a UserID field as well, the query would look something like this:
SELECT u.*
FROM [1st_database_name].[dbo].[tbl_Users] u
INNER JOIN [piscara].[dbo].[tbl_UsersBelongsTo] a
ON u.UserID = a.UserID
WHERE NOT EXISTS (SELECT 1
FROM [1st_database_name].[dbo].[CaseTable] ct
WHERE c.tUserID = u.UserID
AND ct.CreationDate BETWEEN'2019-01-01' AND'2019-12-31'
)
AND a.RoleID=6;
You can also try putting the 2nd query in the WHERE clause, as a sub-query, like so:
SELECT u.*
FROM [1st_database_name].[dbo].[tbl_Users] u
WHERE NOT EXISTS (SELECT 1
FROM [1st_database_name].[dbo].[CaseTable] ct
WHERE c.tUserID = u.UserID
AND ct.CreationDate BETWEEN'2019-01-01' AND'2019-12-31'
)
AND u.UserID IN (SELECT UserID
FROM [piscara].[dbo].[tbl_UsersBelongsTo]
WHERE RoleID=6);

SQL SELECT statement with users, roles and rights

Lets say that I have tables:
Users
Users_in_Roles
Roles
Rights_in_Roles
Rights
Keys are standard( UserFk, RoleFk, RightFk)
The question is: how to get all users that are in role with right X (id = 100)
I Have no idea how to touch this problem. Please help and sorry for my english.
SELECT [dbo].[System_Users].[Id]
,[UserName]
,[FirstName]
,[LastName]
,[Email]
,RoleFk
,[dbo].[System_Roles].Name
FROM [dbo].[System_Users]
INNER JOIN [dbo].[System_Roles_System_Users]
ON [dbo].[System_Roles_System_Users].UserFk = [dbo].[System_Users].Id
INNER JOIN [dbo].[System_Roles]
ON [dbo].[System_Roles].Id = [dbo].[System_Roles_System_Users].RoleFk
WHERE ?
I tryied sth like that, could you tell me what iw wrong?
SELECT
DISTINCT System_Users.Id,
System_Users.FullName
FROM System_Users
INNER JOIN Dict_Rights_System_Users
ON System_Users.Id = Dict_Rights_System_Users.UserFk
INNER JOIN System_Roles_System_Users
ON System_Roles_System_Users.UserFk = System_Users.Id
WHERE
RightFk = 136
OR
136 IN (SELECT Dict_Rights_System_Roles.RightFk FROM Dict_Rights_System_Roles WHERE
Dict_Rights_System_Roles.RoleFk = System_Roles_System_Users.RoleFk)
ORDER BY System_Users.FullName ASC
You will need to JOIN the tables on the key relationships. The basic structure will be:
select u.Id,
u.UserName,
u.FirstName,
u.LastName,
u.Email,
r.RoleFk,
r.Name RoleName,
rt.Name RightName
from users u
inner join users_in_roles ur
on u.id = ur.userfk
inner join roles r
on ur.rolefk = r.id
inner join rights_in_roles rr
on r.rolefk = rr.rolefk
inner join rights rt
on rr.rightfk = rt.id
where rt.id = 100
If you need help learning JOIN syntax here is a great reference:
A Visual Explanation of SQL Joins
You can try with this:
SELECT *
FROM Users u
WHERE EXISTS (
SELECT ur.RoleFk
FROM Users_in_Roles ur
WHERE u.UserPk = ur.UserFk
AND EXISTS
(
SELECT 1
FROM Rights_in_Roles rr
WHERE rr.RoleFk = ur.RoleFk
AND rr.RightFk = 100
)
)
OR EXISTS (
SELECT 1
FROM Users_Rights uri
WHERE u.UserPk = uri.UserFk
AND uri.RightFk = 100
)
Note that the above query doesn't return RoleFk and Name for the role.
Another approach would be:
SELECT u.Id
,u.UserName
,u.FirstName
,u.LastName
,u.Email
,rr.RoleFk
,r.Name
FROM Users u
-- get users that are in role that has right
LEFT JOIN
Users_in_Roles ur ON
ur.UserFk = u.UserPk
LEFT JOIN
Rights_in_Roles rr ON
rr.RoleFk = ur.RoleFk
AND rr.RightFk = 100
LEFT JOIN
Rights r ON
r.RolePk = rr.RoleFk
-- get users that have a right granted to them directly
LEFT JOIN
Users_Rights uri ON
u.UserPk = uri.UserFk
AND uri.RightFk = 100
WHERE rr.RoleFk IS NOT NULL OR uri.UserFk IS NOT NULL

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 = ?

SQL SELECT with m:n relationship

I have m:n relationship between users and tags. One user can have m tags, and one tag can belong to n users. Tables look something like this:
USER:
ID
USER_NAME
USER_HAS_TAG:
USER_ID
TAG_ID
TAG:
ID
TAG_NAME
Let's say that I need to select all users, who have tags "apple", "orange" AND "banana". What would be the most effective way to accomplish this using SQL (MySQL DB)?
SELECT u.*
FROM (
SELECT user_id
FROM tag t
JOIN user_has_tag uht
ON uht.tag_id = t.id
WHERE tag_name IN ('apple', 'orange', 'banana')
GROUP BY
user_id
HAVING COUNT(*) = 3
) q
JOIN user u
ON u.id = q.user_id
By removing HAVING COUNT(*), you get OR instead of AND (though it will not be the most efficient way)
By replacing 3 with 2, you get users that have exactly two of three tags defined.
By replacing = 3 with >= 2, you get users that have at least two of three tags defined.
In addition to the other good answers, it's also possible to check the condition in a WHERE clause:
select *
from user u
where 3 = (
select count(distinct t.id)
from user_has_tag uht
inner join tag t on t.id = uht.tag_id
where t.name in ('apple', 'orange', 'banana')
and uht.user_id = u.userid
)
The count(distinct ...) makes sure a tag is counted only once, even if the user has multiple 'banana' tags.
By the way, the site fruitoverflow.com is not yet registered :)
You can do it all with joins...
select u.*
from user u
inner join user_has_tag ut1 on u.id = ut1.user_id
inner join tag t1 on ut1.tag_id = t1.id and t1.tag_name = 'apple'
inner join user_has_tag ut2 on u.id = ut2.user_id
inner join tag t2 on ut2.tag_id = t2.id and t2.tag_name = 'orange'
inner join user_has_tag ut3 on u.id = ut3.user_id
inner join tag t3 on ut3.tag_id = t3.id and t3.tag_name = 'banana'
SELECT *
FROM USER u
INNER JOIN USER_HAS_TAG uht
ON u.id = uht.user_id
INNER JOIN TAG t
ON uht.TAG_ID = t.ID
WHERE t.TAG_NAME IN ('apple','orange','banana')