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

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);

Related

SQL filtering multiple joins by row-specific date window

I am trying to obtain aggregate stats of each customer in their first 60 days. Each user has a different join date, which resides in a user_info table. Currently, the best way I have of doing this is to repeatedly join to the user table each time I need to get aggregate stats from another table, then joining each pair together in a nested subquery. With multiple tables, this query becomes very sluggish and unwieldy. How can I do this in a more parsimonious manner?
My current solution:
SELECT t1.userid
,t1.total_transactions
,t1.days_transact
,t2.total_vouchers
,t2.days_redeemed
FROM (
SELECT u.userid
,SUM(s.transactions) AS total_transactions
,COUNT(DISTINCT s.dated) AS days_transact
FROM (
SELECT userid
,created
FROM schema.user_info
) u
LEFT JOIN (
SELECT userid
,transactions
,dated
FROM schema.transactions
) s
ON u.userid = s.userid
AND s.dated BETWEEN u.created AND DATE_ADD(u.created, 61)
GROUP BY u.userid
) t1
LEFT JOIN (
SELECT u.userid
,SUM(v.vouchers) AS total_vouchers
,COUNT(DISTINCT s.dated) AS days_redeemed
FROM (
SELECT userid
,created
FROM schema.user_info
) u
LEFT JOIN (
SELECT userid
,vouchers
,dated
FROM schema.vouchers
) v
ON u.userid = v.userid
AND v.dated BETWEEN u.created AND DATE_ADD(u.created, 61)
GROUP BY u.userid
) t2
ON t1.userid = t2.userid

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.

How to eliminate 'The multi-part identifier "" could not be bound' error?

I have this query, it's supposed to return results of non validated accounts in a database, that were created after a certain date. I keep getting this error and I'm not sure how to eliminate it. Here is the query:
select count(*) (nolock)
from dbo.[User]
where ID is not null
and UserStatusID!=2
and CreateDateTime>='5/1/2012'
and not exists (select userid from dbo.UserValidation where dbo.[User].UserID=dbo.UserValidation.UserID)
It errors out on the "where dbo.[User].UserID=dbo.UserValidation.UserID" What am I doing wrong here?
Try aliasing the tables:
select count(*) (nolock)
from dbo.[User] u
where ID is not null
and UserStatusID != 2
and CreateDateTime >= '5/1/2012'
and not exists (select uv.userid from dbo.UserValidation uv where u.UserID = uv.UserID)
Without the schema:
select count(*) (nolock)
from [User] u
where ID is not null
and UserStatusID != 2
and CreateDateTime >= '5/1/2012'
and not exists (select uv.userid from UserValidation uv where u.UserID = uv.UserID)
While doing a JOIN it's always better to explicitly qualify all the columns in query like below.
select count(u.userid)
from [User] u
where u.ID is not null
and u.UserStatusID != 2
and u.CreateDateTime >= '5/1/2012'
and not exists
(
select uv.userid
from UserValidation uv
where uv.UserID = u.UserID
)

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

Joining to a table with multiple rows for the join item

I have a table users which has a primary key userid and a datetime column pay_date.
I've also got a table user_actions which references users via the column userid, and a datetime column action_date.
I want to join the two tables together, fetching only the earliest action from the user_actions table which has an action_date later than or equal to pay_date.
I'm trying things like:
select users.userid from users
left join user_actions on user_actions.userid = users.userid
where user_actions.action_date >= users.pay_date
order by user_actions.pay_date
But obviously that returns me multiple rows per user (one for every user action occurring on or after pay_date). Any idea where to go from here?
Apologies for what probably seems like a simple question, I'm fairly new to t-sql.
CROSS APPLY is your friend:
select users.*, t.* from users
CROSS APPLY(SELECT TOP 1 * FROM user_actions WHERE user_actions.userid = users.userid
AND user_actions.action_date >= users.pay_date
order by user_actions.pay_date) AS t
If you have a PRIMARY KEY on user_actions:
SELECT u.*, ua.*
FROM users u
LEFT JOIN
user_actions ua
ON user_actions.id =
(
SELECT TOP 1 id
FROM user_actions uai
WHERE uai.userid = u.userid
AND uai.action_date >= u.pay_date
ORDER BY
uai.action_date
)
If you don't:
WITH j AS
(
SELECT u.*, ua.*, ROW_NUMBER() OVER (PARTITION BY ua.userid ORDER BY ua.action_date) AS rn, ua.action_date
FROM users u
LEFT JOIN
user_actions ua
ON ua.userid = u.userid
AND ua.action_date >= u.pay_date
)
SELECT *
FROM j
WHERE rn = 1 or action_date is null
Update:
CROSS APPLY proposed by #AlexKuznetsov is more elegant and efficient.
select u.*, ua.* from
users u join users_actions ua on u.userid = ua.userid
where
ua.action_date in
(select min(action_date) from user_actions ua1
where
ua1.action_date >= u.pay_date and
u.userid=ua1.userid)