SQL Query to check if joining record exists - sql

I have 2 tables - User table (tblUsers) and Logs table (tblLogs).
The User table contains - UserID and UserName.
The Logs table contains - UserID, ApplicationID, ApplicationName, LogonDateTime
I want to select all the Users and find if they have accessed a particular application (for example ApplicationID = 3)
Output expected
UserID, UserName, Status
Status - If he has visited the applicationID = 3 or not.

Since you are missing a third table which contain all the applications available ( i guess it's [tblApplications] ), the sql would be:
SELECT
U.UserID
, U.UserName
, (
CASE WHEN L.ApplicationID IS NOT NULL
THEN '1'
ELSE '0'
END
) AS Status
FROM tblUsers AS U, tblApplications AS A
LEFT OUTER JOIN tblLogs AS L
ON A.ID = L.ApplicationID
WHERE A.ID = '3'

Use a CASE expression to check the status.
Query
select t1.UserId, t1.UserName,
max(case when t1.UserId in
(select UserId from tblLogs where ApplicationID = 3) then 'Visited'
else 'Not Visited' end) as `status`
from tblUsers t1
left join tblLogs t2
on t1.UserID = t2.UserID
group by t1.UserId, t1.UserName;
SQl Fiddle Demo

I hope this query give you the direction of thought as to what you exact requirement is:
SELECT u.UserID,
u.UserName,
(SELECT (CASE
WHEN COUNT(*) > 0 THEN
'VISITED'
ELSE
NULL
END)
FROM tblLogs l
WHERE u.UserId = l.UserId
AND l.ApplicationID = '3')
AS status
FROM tblUsers u
Note that based on the DB that you use, the group by on UserName and ApplicationName might be optional.

Related

Display multiple records in one 1 row with multiple columns

I have a table that stores user info such as:
I need to write a query that returns the results in the following format:
I tried doing a LEFT JOIN for each status but that didn't work, any thoughts on how to get the expected results?
if number of statuses is fixed you can do this
Select
id user_id,
open_status,
open_status_date,
inprogress_status,
inprogress_status_date,
complete_status,
complete_status_date
from
(select user_id id from yourTable group by user_id) U left join
(select user_id id, status open_status, status_date open_status_date
from yourTable where status = 'Open') O on U.id = O.id left join
(select user_id id, status inprogress_status, status_date inprogress_status_date
from yourTable where status = 'InProgress') P on U.id = P.id left join
(select user_id id, status complete_status, status_date complete_status_date
from yourTable where status = 'Complete') C on U.id = C.id
Order by id
Break into inline views and join. But this may be not the most efficient way.
ALSO NOTE: if each user definitely has at least "Open" status, you can skip first U inline view and start with O
You want to use a pivot, like this:
select * from test
PIVOT(
max(status_date)
FOR status
IN (
'Open',
'In Progress',
'Complete'
)
)
order by user_id
Suppose every user has a "Open" status
With
open as (select * from table where status = 'Open'),
inp as (select * from table where status = 'In Progress'),
comp as (select * from table where status = 'Complete')
select o.user_id,o.status open_status, o.status_date open_status_date,i.status InProgress_status, i.status_date InProgress_status_date,c.status complete_status, c.status_date complete_status_date
from open o, inp i, comp c
where o.user_id=i.user_id(+)
and o.user_id=c.user_id(+)

SQL query to conditionally select a field value

I have an SQL query that joins 3 tables to return me the required data. The query is as follows:
SELECT (s.user_created,
u.first_name,
u.last_name,
u.email,
s.school_name,
p.plan_name,
substring( a.message, 11), u.phone1)
FROM cb_school s
inner join ugrp_user u
on s.user_created = u.user_id
inner join cb_plan p
on s.current_plan = p.plan_id
inner join audit a
on u.user_id = a.user_id
where s.type = 'sample'
and a.module_short = 'sample-user'
and s.created_time > current_timestamp - interval '10 day';
The query works fine if all the attributes are present. Now for few of my rows, the following value would be a.module_short = 'sample-user' missing. But since I have included it as an AND condition, those rows will not be returned. I am trying to return an empty string for that field if it is present, else the value as per my current query. Is there any way to achieve this.
Think you could possibly use a CASE WHEN statement, like this:
SELECT CASE WHEN a.module_short = 'sample-user' THEN a.module_short
ELSE '' END AS a.module_short
FROM TableA
you can use COALESCE it returns the first not null.
SELECT COALESCE(a.module_short,'')
FROM TableA AS a
SELECT (s.user_created,
u.first_name,
u.last_name,
u.email,
s.school_name,
p.plan_name,
substring( a.message, 11), u.phone1)
FROM cb_school s
INNER JOIN ugrp_user u
ON s.user_created = u.user_id
INNER JOIN cb_plan p
ON s.current_plan = p.plan_id
INNER JOIN audit a
ON u.user_id = a.user_id
AND a.module_short = 'sample-user'
WHERE s.type = 'sample'
AND s.created_time > current_timestamp - interval '10 day';
You want to show all users that have at least one module_short.
If the module_short contains 'sample-user' then it should show it, else it should show NULL as module_short. You only want 1 row per user, even if it has multiple module_shorts.
You can use a CTE, ROW_NUMBER() and the CASE clause for this question.
Example Question
I have 3 tables.
Users: Users with an ID
Modules: Modules with an ID
UserModules: The link between users and modules. You user can have multiple models.
I need a query that returns me all users that have at least 1 module with 2 columns UserName and ModuleName.
I only one 1 row for each user. The ModuleName should only display SQL if the user has that module. Else it should display no module.
Example Tables:
Users:
id name
1 Manuel
2 John
3 Doe
Modules:
id module
1 StackOverflow
2 SQL
3 StackExchange
4 SomethingElse
UserModules:
id module_id user_id
1 1 2
2 1 3
4 2 2
5 2 3
6 3 1
7 3 3
8 4 1
9 4 3
Example Query:
with CTE as (
select
u.name as UserName
, CASE
WHEN m.module = 'SQL' THEN 'SQL' ELSE NULL END as ModuleName
, ROW_NUMBER() OVER(PARTITION BY u.id
ORDER BY (CASE
WHEN m.module = 'SQL' THEN 'Ja' ELSE NULL END) DESC) as rn
from UserModules as um
inner join Users as u
on um.user_id = u.id
inner join Modules as m
on um.module_id = m.id
)
select UserName, ModuleName from CTE
where rn = 1
Example Result:
UserName ModuleName
Manuel NULL
John SQL
Doe SQL
Your query would look like this:
with UsersWithRownumbersBasedOnModule_short as (
SELECT s.user_created,
u.first_name,
u.last_name,
u.email,
s.school_name,
p.plan_name,
substring( a.message, 11),
u.phone1)
CASE
WHEN a.module_short = 'sample-user'
THEN a.module_short
ELSE NULL
END AS ModuleShort
ROW_NUMBER() OVER(PARTITION BY u.user_id ORDER BY (
CASE
WHEN a.module_short = 'sample-user'
THEN a.module_short
ELSE NULL
END) DESC) as rn
FROM cb_school s
inner join ugrp_user u
on s.user_created = u.user_id
inner join cb_plan p
on s.current_plan = p.plan_id
inner join audit a
on u.user_id = a.user_id
where s.type = 'sample'
and s.created_time > current_timestamp - interval '10 day';)
select * from UsersWithRownumbersBasedOnModule_short
where rn = 1
PS: I removed a lose bracket after SELECT and your SUBSTRING() is missing 1 parameter, it needs 3.

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
)

Better ways to write this SQL Query

I have the below table structure
Users (PK - UserId)
System (PK - SystemId)
SystemRoles (PK-SystemRoleId, FK - SystemId)
UserRoles (PK-UserId & SystemRoleId, FK-SystemRoleId, FK-UserId)
Users can have access to different Systems and one System can have different SystemRoles defined.
Now, I need to delete Users who have SystemRoles assigned to them ONLY for a specific System(s). If they have SystemRoles defined for other Systems, they should not be deleted.
I have come up the below query to identify the records that are eligible for delete but think this can surely be optimized. Any suggestions?
SELECT U.*
FROM
(
SELECT
distinct UR.UserID
FROM
dbo.UserRole UR
INNER JOIN dbo.SystemRole SR ON (SR.SystemRoleID = UR.SystemRoleID)
INNER JOIN dbo.[System] S ON (S.SystemID = SR.SystemID)
WHERE
S.SystemName = 'ABC' OR S.SystemName = 'XYZ'
) T
INNER JOIN dbo.[User] U ON (U.UserID = T.UserID)
WHERE T.UserID NOT IN
(
select
distinct UR.UserID
from
dbo.[UserRole] UR
INNER JOIN dbo.SystemRole SR ON (SR.SystemRoleID = UR.SystemRoleID)
INNER JOIN dbo.[System] S ON (S.SystemID = SR.SystemID)
WHERE
S.SystemName <> 'ABC'
AND S.SystemName <> 'XYZ'
)
something like this?
select userid from (
SELECT
UR.UserID,
max(case when (S.SystemName = 'ABC' OR S.SystemName = 'XYZ')
then 1 else 0 end) as kill,
max(case when (S.SystemName <> 'ABC' AND S.SystemName <> 'XYZ')
then 1 else 0 end) as keep
FROM
dbo.UserRole UR
INNER JOIN dbo.SystemRole SR ON (SR.SystemRoleID = UR.SystemRoleID)
INNER JOIN dbo.[System] S ON (S.SystemID = SR.SystemID)
group by UR.UserID
) u where kill = 1 and keep = 0
This sort of structure will get you the records you need.
select yourfields -- or delete
from userroles
where userid in
(select userid
from userroles join etc
where system.name = the one you want
except
select userid
from userroles join etc
where system.name <> the one you want
)

sql: want to select record from table 1, based on matching (or no matching) record in table2

I have two tables. They are linked together with a userid. What i wanted do is check if there's a linked record in table 2 AND if a date field in that table is older then a certain time OR if there's no linked record in the 2nd table.
I thought i wanted a left join, but it's ignoring the date field. If there are no records in the 2nd table, then i want to ignore the 2nd table. But if there is a matching record in the 2nd table and the date is outside of my range, then i dont want to select those records.
SELECT FirstName, Email, u.userid FROM u
LEFT JOIN uevh
ON u.UserID = uevh.UserID AND uevh.LastEmailed < GETDATE()-14
WHERE u.ConfirmedEmail = 0
if i run that, and there's a record in the uevh table that's less then 14 days old, i dont want a record returned, but it's returning a record regardless of the date.
SELECT u.FirstName
, u.Email
, u.userid
FROM u
WHERE NOT EXISTS
( SELECT *
FROM uevh
WHERE u.UserID = uevh.UserID
AND NOT (uevh.LastEmailed < GETDATE()-14)
)
AND u.ConfirmedEmail = 0
or:
SELECT u.FirstName
, u.Email
, u.userid
FROM u
WHERE ( EXISTS
( SELECT *
FROM uevh
WHERE u.UserID = uevh.UserID
AND uevh.LastEmailed < GETDATE()-14
)
OR NOT EXISTS
( SELECT *
FROM uevh
WHERE u.UserID = uevh.UserID
)
)
AND u.ConfirmedEmail = 0
or:
SELECT u.FirstName
, u.Email
, u.userid
FROM u
LEFT JOIN uevh
ON u.UserID = uevh.UserID
WHERE u.ConfirmedEmail = 0
AND ( uevh.LastEmailed < GETDATE()-14
OR uehv.UserID IS NULL
)
You could try something like this. This is untested but should work.
Declare #date datetime;
Set #date='20110101';
Select *
from tbl1
Left outer join tbl2 on tbl1.Id =tbl2.Id
and #date > coalesce(tbl2.date,dateadd(day,1,#date));