Replace SQL Query - sql

I want to replace this Query
DECLARE #tmpUser TABLE( UserId INT, UserType INT )
INSERT INTO #tmpUser
SELECT
u.UserId,
1
FROM
Users u (nolock)
WHERE
u.UPIN = #AttendingDoctorID
IF ##ROWCOUNT = 0
BEGIN
INSERT INTO #tmpUser
SELECT
u.UserId,
1
FROM
Users u (nolock)
WHERE
u.FirstName = #AttendingDoctorFirstName AND
u.LastName = #AttendingDoctorLastName
END
SELECT * FROM #tmpUser
to a single SQL SELECT statement without using #tmpUser or any other temp tables

The following uses an extra lookup, but it fulfills your requirements. I don't know for sure if this is any more efficient than using a temp table, though if UPIN is indexed then I suspect it would be.
IF EXISTS(
SELECT
1
FROM
Users u
WHERE
u.UPIN = #AttendingDoctorID)
BEGIN
SELECT
u.UserId, 1
FROM
Users u WITH(nolock)
WHERE
u.UPIN = #AttendingDoctorID
END ELSE BEGIN
SELECT
u.UserId,
1
FROM
Users u (nolock)
WHERE
u.FirstName = #AttendingDoctorFirstName AND
u.LastName = #AttendingDoctorLastName
END

How about Using OR instead of two separate queries.
I think it would serve the purpose.
SELECT
u.UserId, 1
FROM
Users u (nolock)
WHERE
(u.UPIN = #AttendingDoctorID)
OR
(u.FirstName = #AttendingDoctorFirstName AND
u.LastName = #AttendingDoctorLastName)

Related

How convert multiple results rows to single row SQL with respective values

I have two tables Users and UserAttributes in SQL DB.
Table have structure like
Out required is
My attempted query
Select * from (
Select u.UserId,u.Username,u.UserEmail,ua.objectName,ua.objectValue
from Users u join userAttribute on u.UserId=ua.UserId
where ua.objectName='city' or pv.objectName='phone')) results
PIVOT (count(UserId)
For objectName IN ([city],[phone])) As pivot_table;
Current output
Still it is an double entry also for the reason I am not aware the city and phone results are in 0 and 1 (boolean).
Please help getting the desired output.
You can do aggregation :
select u.UserId, u.username, u.usermemail,
max(case when ua.objectname = 'city' then objvalue end),
max(case when ua.objectname = 'phone' then objvalue end)
from User u join
userAttribute ua
on u.UserId = ua.UserId
group by u.UserId, u.username, u.usermemail;
You can use conditional aggregation to extract the values from the UserAttributes table:
SELECT u.userid, u.userName, u.userEmail,
MAX(CASE WHEN ua.objName = 'city' THEN ua.objValue END) AS city,
MAX(CASE WHEN ua.objName = 'phone' THEN ua.objValue END) AS phone,
FROM Users u
JOIN UserAttributes ua ON ua.userid = u.userid
GROUP BY u.userid, u.userName, u.userEmail
To pivot just two columns, joins is convenient:
select u.*, uac.objvalue as city, uap.objvalue as phone
from users u left join
userattributes uac
on uac.userid = u.userid and
uac.objname = 'city' left join
userattributes uap
on uap.userid = u.userid and
uap.objname = 'phone';
If you want only rows with matches, then add:
where uac.objvalue is not null or uap.objvalue is not null
If you have more columns, then join and group by is probably a better approach:
select u.*, au.*
from users u join
(select ua.userid,
max(case when objname = 'city' then objvalue end) as city,
max(case when objname = 'phone' then objvalue end) as phone
from userattributes ua
group by ua.userid
) ua
on u.userid = ua.userid;
Please try following query. Hope this will help
DECLARE #Users table(userid int, username varchar(50))
insert into #Users select 1,'Shiv'
insert into #Users select 2,'Ajay'
DECLARE #UserAttr table(userid int,objname varchar(100),objvalue varchar(100))
insert into #UserAttr select 1,'City','Chd'
insert into #UserAttr select 1,'phone','9646XXXX'
insert into #UserAttr select 2,'phone','8985XXXX'
select * FROM
(
select u.userid,u.username,ua.objname,ua.objvalue FROM #Users u
join #UserAttr ua on u.userid = ua.userid
)a pivot (min(a.objvalue) FOR a.objname in ([city],[phone]))p
Below is the query which you can try with or without pivot.
1. Without Pivot:-
SELECT ur.UserId,ur.UserName,ur.UserEmail,
Max(CASE WHEN ua.objName = 'city' THEN ua.ObjValue END) as city,
Max (CASE WHEN ua.ObjName = 'phone' THEN ua.ObjValue END) as Phone
FROM Users ur
JOIN UserAttribute ua ON ur.UserId = ua.UserId
GROUP BY ur.UserId,ur.UserName,ur.UserEmail
2. With Pivot :-
select * from (select us.UserId,us.UserName,us.UserEmail,ua.ObjName,ua.ObjValue from users us inner join UserAttribute ua on us.UserId=ua.UserId)t pivot
(
max(ObjValue)
For ObjName in ([city],[phone])
) as pivot_table;
Please try this query and let me know the results.

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
)

How to simplify a query between Many to Many Relationship

I was wondering how to simplify this query, here(http://sqlfiddle.com/#!3/2789c/4) you have the complete example
SELECT distinct (R.[roleId])
FROM [Role] R
LEFT JOIN [userRole] U ON R.[roleId] = U.[roleId]
WHERE R.RoleID NOT IN(
SELECT [roleId]
from [dbo].[userRole]
WHERE userId = 2)
I want to get all the roles that are not assigned to an specific user. I think the inner select could be erase.
Update 1
After your great help, I could use only one SELECT http://sqlfiddle.com/#!3/2789c/87
SELECT R.[roleID]
FROM [Role] R
LEFT JOIN [userRole] U
ON R.[roleID] = U.[roleID] AND U.userId = #userID
WHERE U.userId IS NULL
As simple as it gets:
select roleId
from Role
except
select roleId
from userRole
where userId = 2
SELECT R.roleId
FROM [Role] R
LEFT JOIN [userRole] U ON R.roleId = U.roleId
group by r.roleId
having sum(case when U.userId = 2 then 1 else 0 end) = 0
SQLFiddle demo
You can also try this
Select distinct role.roleID from role , userrole
except
select roleId from userrole where userID=2
OR
SELECT R.roleId
FROM [Role] R
LEFT JOIN [userRole] U ON R.roleId = U.roleId
except
select roleId from userrole where userID=2

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

SELECT INTO inside a subquery

Environment : SQL Server 2005
I have this type of request
SELECT *
FROM user
WHERE EXISTS(
SELECT *
FROM user_access
WHERE user_access.user_id = user.user_id
AND user_access.access IN ('HIGH_LEVEL','MEDIUM_LEVEL')
)
AND EXISTS(
SELECT *
FROM user_service
WHERE user_service.role ='Manager'
AND user.id_user = user_service.user_id
)
AND user.is_active ='True'
But in the result I'd also like to get the result from the subquery, so I though about something like
DECLARE #user_access TABLE(user_id int,access nvarchar(30))
DECLARE #user_service TABLE(user_id int,service_id int,role nvarchar(10))
SELECT * FROM user
WHERE EXISTS(
SELECT user_id,access INTO [#user_access]
FROM user_access
WHERE user_access.user_id = user.user_id
AND user_access.access IN ('HIGH_LEVEL','MEDIUM_LEVEL')
)
AND EXISTS(
SELECT user_id , service_id,role INTO #user_service
FROM user_service
WHERE user_service.role ='Manager'
AND user.id_user = user_service.user_id
)
AND user.is_active ='True'
SELECT * FROM #user_acess
select * from #user_service
But I get the following error :
"Incorrect syntax near the keyword 'INTO'."
Do you have any idea how I can do it without doing twice the subquery (I tried with a temp table, same error) ?
EDIT : I'm sorry for my fellow who tried to solve my problem, I forget one thing : I have 2 subquery. See the requests. I want :
All the active user, with high or medium access and who are manager
All the service in which these users are
All the access of these users
A temp table or variable would appear to be the simplest solution. Stuff the results of the subquery into a temp table or variable and then execute two select statements at the end of your procedure.
Declare #UserAccesses Table (
user_id ...
, access varchar(...)
)
Insert #UserAccesses( user_id, access )
Select UA.user_id, UA.access
From user_access As UA
Join user As U
On U.user_id = UA.user_Id
Where U.is_active = 'True'
And UA.access In('HIGH_LEVEL', 'MEDIUM_LEVEL')
Select ...
From user
Where Exists (
Select 1
From #UserAccess As UA1
Where UA1.user_id = user.user_id
)
Select user_id, access
From #UserAccesses
Update given your expansion of the original question
The solution for two subqueries is essentially the same as with one except that instead of returning two resultsets, you return three. As before, you use a temp table/variable per subquery:
Declare #UserAccesses Table (
user_id int
, access nvarchar(30)
)
Declare #UserServices Table (
user_id int
, service_id int
, role nvarchar(10)
)
Insert #UserAccesses( user_id, access )
Select UA.user_id, UA.access
From user_access As UA
Join user As U
On U.user_id = UA.user_Id
Where U.is_active = 'True'
And UA.access In('HIGH_LEVEL', 'MEDIUM_LEVEL')
Insert #UserServices( user_id, service_id, role )
Select user_id , service_id,role
From user_service
Where user_service.role ='Manager'
And Exists (
Select 1
From #UserAccesses As UA1
Where UA1.user_id = user_service.user_id
)
Select ...
From user
Where Exists (
Select 1
From #UserServices As US1
Where US1.user_id = user.user_id
)
Select user_id, access
From #UserAccesses As UA
Where Exists (
Select 1
From #UserServices As US1
Where US1.user_id = UA.user_id
)
Select user_id, access
From #UserServices
You can pull the subquery out to make the temp table, then do the two queries you want out of the temp table.
This is about the best you can do to optimize. There is no way to capture and retain the data in the EXISTS subqueries - especially when EXISTS does not fully evaluate the result set. It short circuits when a SINGLE match is found in the subquery (one access for the user) so you can't get all the access records from it anyway.
declare #user table (user_id int)
insert #user
SELECT [user].user_id
FROM [user]
WHERE EXISTS(
SELECT *
FROM user_access
WHERE user_access.user_id = [user].user_id
AND user_access.access IN ('HIGH_LEVEL','MEDIUM_LEVEL')
)
AND EXISTS(
SELECT *
FROM user_service
WHERE user_service.role ='Manager'
AND [user].id_user = user_service.user_id
)
AND [user].is_active ='True'
SELECT [user].* FROM [user] inner join #user u on u.USER_ID = [user].user_id
SELECT a.user_id, a.access FROM user_access a inner join #user u on u.USER_ID = a.user_id
SELECT s.user_id, s.service_id, s.role FROM user_service s inner join #user u on u.USER_ID = s.user_id
Here's an implementation of what Ken Down^ has suggested and I think its result should not contain any extraneous rows, considering what you are after.
DECLARE #user_access TABLE(user_id int,access nvarchar(30))
DECLARE #user_service TABLE(user_id int,service_id int,role nvarchar(10))
INSERT INTO #user_access
SELECT ua.user_id, ua.access
FROM user_access ua
INNER JOIN [user] u ON ua.user_id = u.user_id
WHERE u.is_active ='True'
AND ua.access IN ('HIGH_LEVEL','MEDIUM_LEVEL')
INSERT INTO #user_service
SELECT us.user_id , us.service_id, us.role
FROM user_service us
INNER JOIN [user] u ON us.user_id = u.user_id
WHERE u.is_active ='True'
AND us.role ='Manager'
SELECT u.*
FROM [user] u
INNER JOIN (
SELECT user_id FROM #user_access
UNION
SELECT user_id FROM #user_service
) uas ON uas.user_id = u.user_id
SELECT * FROM #user_acess
SELECT * FROM #user_service
This should return the result set from both tables.
SELECT *
FROM user u, user_access a
WHERE u.user_id in (
SELECT user_access.user_id
FROM user_access
WHERE user_access.user_id = u.user_id
AND user_access.access IN ('HIGH_LEVEL','MEDIUM_LEVEL')
AND user_access.user_id = u.user_id
)
AND a.user_id = u.user_id
AND u.is_active ='True'