sql union with different expressions - sql

I have this sql which doesnt work as both unions have different expressions, how can this be done? thanks (mssql stored procedure) have no idea where to go from here
SELECT
*
FROM
(SELECT
ROW_NUMBER()
OVER
(ORDER BY T.TopicCreationDate desc)
AS RowNumber
,T.TopicId, T.TopicTitle, T.TopicShortName, T.TopicDescription, T.TopicCreationDate, T.TopicViews, T.TopicReplies, T.UserId, T.TopicTags, T.TopicIsClose,
T.TopicOrder, T.LastMessageId, T.UserName, M.MessageCreationDate, T.ReadAccessGroupId, T.PostAccessGroupId, U.UserGroupId, U.UserPhoto, T.UserFullName
,M.UserId AS MessageUserId
,MU.UserName AS MessageUserName
FROM TopicsComplete AS T LEFT OUTER JOIN
Messages AS M ON M.TopicId = T.TopicId AND M.MessageId = T.LastMessageId AND M.Active = 1 INNER JOIN
Users AS U ON U.UserId = T.UserId
LEFT JOIN Users MU ON MU.UserId = M.UserId
WHERE EXISTS
( SELECT *
FROM TopicsComplete
LEFT OUTER JOIN
Messages AS M ON M.TopicId = T.TopicId AND M.MessageId = T.LastMessageId AND M.Active = 1 INNER JOIN
Users AS U ON U.UserId = T.UserId LEFT OUTER JOIN
Users AS MU ON MU.UserId = M.UserId
WHERE (T.UserId = #id)
UNION
SELECT *
FROM TopicsComplete
LEFT OUTER JOIN
Messages AS M ON M.TopicId = T.TopicId AND M.MessageId = T.LastMessageId AND M.Active = 1 INNER JOIN
topicfollows AS TF ON T.TopicId = TF.topicid INNER JOIN
Users AS U ON U.UserId = T.UserId LEFT JOIN
Users MU ON MU.UserId = M.UserId
WHERE (TF.userid = #id)
)
) T

All the columns returned by both sides of the union have to match (the number of columns and their data types, not the actual names/aliases).
First of all, don't specify the returned columns with SELECT *. It's a bad habit that leads to problems, e.g. when you add a new column to one of the tables.
Secondly, if one part of the union doesn't have enough columns, specify some constants to provide values for those columns (adding an alias for clarity), e.g.
SELECT 0 AS RowNumber, T.TopicId, T.TopicTitle, T.TopicShortName, T.TopicDescription, T.TopicCreationDate, T.TopicViews, T.TopicReplies, T.UserId, T.TopicTags, T.TopicIsClose,
T.TopicOrder, T.LastMessageId, T.UserName, M.MessageCreationDate, T.ReadAccessGroupId, T.PostAccessGroupId, U.UserGroupId, U.UserPhoto, T.UserFullName

Related

Combine multiple queries into one query

I am trying to group by clientid and m.id. How can I convert these 2 queries into 1 query?
Query 1
select cs.clientid, count(distinct(m.id)) as cp
into #temp
from call_table cs
join mem_table m
on cs.CLIENTID = m.clientid
join activity_table a
on a.memid = m.id
where a.activity = 'abc'
group by cs.clientid, m.id
Query 2
select clientid, sum(cp) from #temp
group by ClientId
Update
More specifically, I am looking to write this query without using a temp table.
You could group only by clientid
And since no fields from activity_table are selected, an EXISTS can be used instead.
select cs.clientid, count(distinct m.id) as cp
from call_table cs
join mem_table m on m.clientid = cs.clientid
where exists
(
select 1
from activity_table a
where a.memid = m.id
and a.activity = 'abc'
)
group by cs.clientid
Why wouldn't you just write this as:
with temp as (
select cs.clientid, count(distinct m.id) as cp
from call_table cs join
mem_table m
on cs.CLIENTID = m.clientid join
activity_table a
on a.memid = m.id
where a.activity = 'abc'
group by cs.clientid, m.id
)
select clientid, sum(cp)
from temp
group by ClientId;
I can't figure out what the logic should really be, but this seems like the simplest way to combine the queries.
I would speculate that you simply want this query:
select cs.clientid, count(distinct m.id) as cp
from call_table cs join
mem_table m
on cs.CLIENTID = m.clientid join
activity_table a
on a.memid = m.id
where a.activity = 'abc'
group by cs.clientid;
That is, remove m.id from the group by. And this can in turn be simplified to:
select m.clientid, count(distinct m.id) as cp
from mem_table m join
activity_table a
on a.memid = m.id
where a.activity = 'abc'
group by cs.clientid;
Assuming that m.id is unique and non-NULL, this can be further simplified to:
select m.clientid, count(*) as cp
from mem_table m join
activity_table a
on a.memid = m.id
where a.activity = 'abc'
group by cs.clientid;

Get total count using a window function

Hey all this is the query I have so far:
WITH LIMIT AS
(SELECT
U.userID
,U.username
,U.fname
,U.mname
,U.lname
,U.email
,U.active
,S.sName
,S.sID
,T.[value]
,T.trackingNumberID
,SU.primaryLocation
,row_number() OVER (ORDER BY U.userid) AS RN
,COUNT(*) OVER (ORDER BY U.userid) AS CNT
,UR.roleID
FROM
[---].[dbo].[tblUsers] AS U
LEFT OUTER JOIN [---].[dbo].[tblTrackingNumbers] AS T
ON T.userID = U.userID
LEFT OUTER JOIN [---].[dbo].[tblSU] AS SU
ON U.userID = SU.userID
LEFT OUTER JOIN [---].[dbo].[tblS] AS S
ON SU.sID = S.sID
LEFT OUTER JOIN [---].[dbo].[tblUserRoles] AS UR
ON UR.userID = U.userID
LEFT OUTER JOIN [---].[dbo].[tblRoles] AS R
ON UR.roleID = R.roleID
WHERE
U.active = 1
AND
SU.primaryLocation = 1
AND
SU.active = 1
AND
U.orgID = 1
AND
S.ID = 35
AND U.userID IN (SELECT userID
FROM [---].[dbo].[tblSU] AS SU
INNER JOIN [].[dbo].[tblS] AS S
ON S.sID = SU.sID
WHERE
SU.active = 1
AND
S.sID = 35)
) SELECT * FROM LIMIT WHERE RN Between 0 AND 10000
As you can see by the query above I am trying COUNT(*) OVER (ORDER BY U.userid) AS CNT which gives me the same count as RN.
What I need is the total amount of records this would be bringing back (842 rows).
COUNT(*) OVER (ORDER BY U.userid) AS CNT calulates a "running count" - the count until "that" row. If you want to count all rows in the complete result, use the window function without the order by
COUNT(*) OVER () AS CNT
this might sound cuckoo, but i found with large tables you get better performance if you select the count into a variable and then select your records and just add the variable. something with the count(*) over() causes bad performance when tables get too large.
DECLARE #RecordCount INT
SELECT #RecordCount = COUNT(*)
FROM [---].[dbo].[tblUsers] AS U
LEFT OUTER JOIN [---].[dbo].[tblTrackingNumbers] AS T ON T.userID = U.userID
LEFT OUTER JOIN [---].[dbo].[tblSU] AS SU ON U.userID = SU.userID
LEFT OUTER JOIN [---].[dbo].[tblS] AS S ON SU.sID = S.sID
LEFT OUTER JOIN [---].[dbo].[tblUserRoles] AS UR ON UR.userID = U.userID
LEFT OUTER JOIN [---].[dbo].[tblRoles] AS R ON UR.roleID = R.roleID
WHERE U.active = 1
AND SU.primaryLocation = 1
AND SU.active = 1
AND U.orgID = 1
AND S.ID = 35
AND U.userID IN (SELECT userID
FROM [---].[dbo].[tblSU] AS SU
INNER JOIN [].[dbo].[tblS] AS S ON S.sID = SU.sID
WHERE SU.active = 1
AND S.sID = 35)
SELECT U.userID,
U.username,
U.fname,
U.mname,
U.lname,
U.email,
U.active,
S.sName,
S.sID,
T.[value],
T.trackingNumberID,
SU.primaryLocation,
#RecordCount AS CNT,
UR.roleID
FROM [---].[dbo].[tblUsers] AS U
LEFT OUTER JOIN [---].[dbo].[tblTrackingNumbers] AS T ON T.userID = U.userID
LEFT OUTER JOIN [---].[dbo].[tblSU] AS SU ON U.userID = SU.userID
LEFT OUTER JOIN [---].[dbo].[tblS] AS S ON SU.sID = S.sID
LEFT OUTER JOIN [---].[dbo].[tblUserRoles] AS UR ON UR.userID = U.userID
LEFT OUTER JOIN [---].[dbo].[tblRoles] AS R ON UR.roleID = R.roleID
WHERE U.active = 1
AND SU.primaryLocation = 1
AND SU.active = 1
AND U.orgID = 1
AND S.ID = 35
AND U.userID IN (SELECT userID
FROM [---].[dbo].[tblSU] AS SU
INNER JOIN [].[dbo].[tblS] AS S ON S.sID = SU.sID
WHERE SU.active = 1
AND S.sID = 35)
ORDER BY U.userID
OFFSET 0 ROWS FETCH NEXT 10000 ROWS ONLY

SQL Server : JOIN query

I'm newbie in SQL Server and I need some help with SQL Server and JOIN method.
My code is:
SELECT TOP 1000
p.value AS userposition,
p2.value AS usercell,
t.id
FROM
[es] t
JOIN
[user] u ON t.user_uid = u.uid
JOIN
[user] su ON u.superior_uid = su.uid
JOIN
[user_params] up ON t.user_uid = up.user_uid
LEFT JOIN
[params] p ON up.param_id = p.id AND p.name_id = 1
JOIN
[user_params] up2 ON t.user_uid = up2.user_uid
LEFT JOIN
[params] p2 ON up2.param_id = p2.id AND p.name_id = 2
but it returns duplicated records. I want them just as many as rows in [es] table. In MySQL I would use GROUP BY t.id, but in SQL Server that method doesn't work.
Thanks in advance.
EDIT (clarification):
Thank you for your replies. Maybe I should describe my tables structure and what I need to display.
Table [ES]
[id],[user_uid],[more_data]
Table [User]
[uid],[superior_uid],[more_data]
Table [UserParams]
[id],[user_uid],[param_id]
Table [Params]
[id],[param_id],[value]
Now what I need is to get all records from [ES] add user data from [User] add his superior data on [User][superior_uid] which is also an [User] record, add [Params] with [Params][name_id] = 1 as value1 AND add [Params] with [Params][name_id] = 2 as value2 ... through [UserParams] if exists.
I think the problem is with JOIN or GROUP BY. [ES] records with users has no [UserParams] are shown only once, but those with [UserParams] are doubled.I tried LEFT OUTER JOIN but it doesn't work. :(
How about
SELECT DISTINCT TOP 1000
p.value AS userposition,
p2.value AS usercell,
t.id
FROM [es] t
JOIN [user] u ON t.user_uid = u.uid
JOIN [user] su ON u.superior_uid = su.uid
JOIN [user_params] up ON t.user_uid = up.user_uid
LEFT JOIN [params] p ON up.param_id = p.id AND p.name_id = 1
JOIN [user_params] up2 ON t.user_uid = up2.user_uid
LEFT JOIN [params] p2 ON up2.param_id = p2.id AND p.name_id = 2
ORDER BY (whichever rows that you want it to be ordered by) ?
all of your columns need to be in the group by, or part of an aggregate function
p.value AS userposition, #group by or agg func
p2.value AS usercell, #group by or agg func
t.id #group by
Wouldnt be certain without knowing what p.value and p2.value actually mean
Not to sure about the joins but I guess you know what you doing, to select distinct row you can use row_number() function as below
SELECT userposition
,usercell
,id
FROM (
SELECT TOP 1000
p.value AS userposition
,p2.value AS usercell
,t.id
,ROW_NUMBER() OVER (PARTITION BY t.user_uid ORDER BY t.user_uid) rn
FROM [es] t
INNER JOIN [user] u ON t.user_uid = u.[uid]
INNER JOIN [user] su ON u.superior_uid = su.[uid]
INNER JOIN [user_params] up ON t.user_uid = up.user_uid
LEFT JOIN [params] p ON up.param_id = p.id AND p.name_id = 1
INNER JOIN [user_params] up2 ON t.user_uid = up2.user_uid
LEFT JOIN [params] p2 ON up2.param_id = p2.id AND p.name_id = 2
) A
WHERE A.rn = 1
You can get rid of the last 2 joins by combining them, use DISTINCT to not get repeated entries, and use ORDER BY to sort it.
SELECT DISTINCT TOP 1000
p.value AS userposition,
p2.value AS usercell,
t.id
FROM [es] t
JOIN [user] u ON t.user_uid = u.uid
JOIN [user] su ON u.superior_uid = su.uid
JOIN [user_params] up ON t.user_uid = up.user_uid
LEFT JOIN [params] p ON up.param_id = p.id AND (p.name_id = 1 OR p.name_id = 2)
ORDER BY t.id

sql group by clause error

This is the error
Msg 8120, Level 16, State 1, Procedure FollowingUpdates, Line 10
Column 'TopicsComplete.TopicCreationDate' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
This is after adding these 2 lines, I need to count a separate table rows (the amount of rows not the count of the topicid) and include in result any ideas? thanks
,COUNT(DISTINCT MC.topicid) AS NewMessagesCount
LEFT OUTER JOIN Messages AS MC ON MC.TopicId = T.TopicId AND MC.userid = #id
#id int = null
,#UserGroupId int = null
AS
SELECT
*
FROM
(SELECT
ROW_NUMBER()
OVER ( ORDER BY TopicOrder desc
, CASE WHEN M.MessageCreationDate > T.TopicCreationDate
THEN M.MessageCreationDate
ELSE T.TopicCreationDate
END desc )
AS RowNumber,
T.TopicId, T.TopicTitle, T.TopicShortName, T.TopicDescription, T.TopicCreationDate, T.TopicViews, T.TopicReplies, T.UserId, T.TopicTags, T.TopicIsClose, T.TopicOrder, T.LastMessageId, T.UserName, M.MessageCreationDate, T.ReadAccessGroupId, T.PostAccessGroupId, U.UserGroupId, U.UserPhoto, T.UserFullName ,M.UserId AS MessageUserId ,MU.UserName AS MessageUserName
,COUNT(DISTINCT MC.topicid) AS NewMessagesCount
FROM TopicsComplete AS T
LEFT OUTER JOIN Messages AS M ON M.TopicId = T.TopicId AND M.MessageId = T.LastMessageId AND M.Active = 1
LEFT OUTER JOIN Messages AS MC ON MC.TopicId = T.TopicId AND MC.userid = #id
INNER JOIN Users AS U ON U.UserId = T.UserId
LEFT JOIN Users MU ON MU.UserId = M.UserId
WHERE EXISTS
(SELECT * FROM TopicsComplete
LEFT OUTER JOIN Messages AS M ON M.TopicId = T.TopicId AND M.MessageId = T.LastMessageId AND M.Active = 1 INNER JOIN
topicfollows AS TF ON T.TopicId != TF.topicid INNER JOIN
Users AS U ON U.UserId = T.UserId LEFT OUTER JOIN
Users AS MU ON MU.UserId = M.UserId
WHERE (T.UserId = #id)
UNION SELECT * FROM TopicsComplete
LEFT OUTER JOIN Messages AS M ON M.TopicId = T.TopicId AND M.MessageId = T.LastMessageId AND M.Active = 1 INNER JOIN
topicfollows AS TF ON T.TopicId = TF.topicid INNER JOIN
Users AS U ON U.UserId = T.UserId LEFT JOIN
Users MU ON MU.UserId = M.UserId
WHERE (TF.userid = #id)
)
) T
When you have an aggregation function in the select, SQL Server quite reasonably assumes that you want to do an aggregation. All columns not in aggregation functions should then be in the group by clause.
In your case, you have COUNT(DISTINCT MC.topicid) AS NewMessagesCount in a select expression. All the other columns should be in the group by. There is no group by, but you get the error anyway, because one should be there.
You need to have any column not contained in an aggregate (max, min, count, sum, etc.) in the GROUP BY clause.

sql syntax group by

Struggling with this as i'm not good with sql and designer wont work with the OVER use. Basically this is getting a list of topics if the user is following an associated tag.
I need to group by T.TopicId to stop duplicates. If a user is selecting more than one tag associated with a topic it will list the topic twice (once for each tag)
When I add a group by in sql I get multiple errors and i've tried rearranging things and cant get it to work, As said i'm useless with sql statements
#id int = null
AS
SELECT
*
FROM
(SELECT
ROW_NUMBER()
OVER
(ORDER BY TopicOrder desc
,
(CASE
WHEN M.MessageCreationDate > T.TopicCreationDate THEN M.MessageCreationDate
ELSE T.TopicCreationDate
END) desc)
AS RowNumber
,T.TopicId, T.TopicTitle, T.TopicShortName, T.TopicDescription, T.TopicCreationDate, T.TopicViews, T.TopicReplies, T.UserId, T.TopicTags, T.TopicIsClose,
T.TopicOrder, T.LastMessageId, T.UserName, M.MessageCreationDate, M.UserId AS MessageUserId, MU.UserName AS MessageUserName, U.UserGroupId,
U.UserPhoto, T.UserFullName
FROM Tags INNER JOIN
TopicsComplete AS T ON T.TopicId = Tags.TopicId LEFT OUTER JOIN
Messages AS M ON M.TopicId = T.TopicId AND M.MessageId = T.LastMessageId AND M.Active = 1 LEFT OUTER JOIN
Users AS MU ON MU.UserId = M.UserId LEFT OUTER JOIN
Users AS U ON U.UserId = T.UserId LEFT OUTER JOIN
tagfollows AS TF ON #id = TF.userid
WHERE (Tags.Tag = TF.tag)
)T
If anyone could help it would be much appreciated, thanks! :)
I think you only need to convert the join to tagfollows into an EXISTS subquery (and remove the redundant nesting):
SELECT
ROW_NUMBER()
OVER ( ORDER BY TopicOrder desc
, CASE WHEN M.MessageCreationDate > T.TopicCreationDate
THEN M.MessageCreationDate
ELSE T.TopicCreationDate
END desc )
AS RowNumber,
T.TopicId, T.TopicTitle, T.TopicShortName, T.TopicDescription,
T.TopicCreationDate, T.TopicViews, T.TopicReplies, T.UserId,
T.TopicTags, T.TopicIsClose, T.TopicOrder, T.LastMessageId,
T.UserName, M.MessageCreationDate,
M.UserId AS MessageUserId,
MU.UserName AS MessageUserName,
U.UserGroupId, U.UserPhoto, T.UserFullName
FROM
TopicsComplete AS T
LEFT OUTER JOIN
Messages AS M ON M.TopicId = T.TopicId
AND M.MessageId = T.LastMessageId
AND M.Active = 1
LEFT OUTER JOIN
Users AS MU ON MU.UserId = M.UserId
LEFT OUTER JOIN
Users AS U ON U.UserId = T.UserId
WHERE EXISTS
( SELECT *
FROM Tags
INNER JOIN tagfollows AS TF
ON Tags.Tag = TF.tag
WHERE T.TopicId = Tags.TopicId
AND #id = TF.userid
) ;
You say you want to show posts with tags in the set that the user is following, but you don't want the post to show up multiple times when it has multiple matching tags. That's a perfect use for an EXISTS subquery. Here's an example from that MSDN page.
SELECT a.FirstName, a.LastName
FROM Person.Person AS a
WHERE EXISTS
(SELECT *
FROM HumanResources.Employee AS b
WHERE a.BusinessEntityID = b.BusinessEntityID
AND a.LastName = 'Johnson');
You're really interested in the person table (like your posts table), but you want to show records that have at least one matching record in employee (like your tags table).