SELECT INTO inside a subquery - sql

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'

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

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.

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

Replace SQL Query

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)