I've successfully built messaging functionality into my application, I have a small issue which I need assistance on, when I send an email to another user I insert this message into the database, the column ReadDate for this row will be null due to it being new, when I read this message from the UI the ReadDate column will then be populated with today's date.
Now my issue is this, given the below query, which returns emails inside someones inbox where ReceipientId = #UserId passed in. :
Select p.Id [SenderId], p.Username, count(mr.RecipientId) [TotalMessages], up.PhotoId,
CASE
WHEN mr.ReadDate is null then 1 -- New message
ELSE 0 -- Message has been read
END AS NewMessage,
p.LastLoggedIn, p.LoggedIn
FROM [User].[User_Profile] p
JOIN [MailBox].[Message] m on p.Id = m.SenderId
JOIN [MailBox].[MessageRecipient] mr on m.Id = mr.MessageId
LEFT JOIN [User].[User_Photos] up on p.Id = up.UserId
where up.isProfilePic = 1 and mr.RecipientId = #UserId and mr.DeletedDate is null
GROUP BY p.id, p.Username, mr.RecipientId, up.PhotoId, p.LastLoggedIn, p.LoggedIn,
CASE
WHEN mr.ReadDate is null then 1 -- New message
ELSE 0 -- Message has been read
END;
When I execute this I get returned the following:
As you can see its the same user, difference being I have a NewMessage i.e 1 which is causing this user to appear twice, If I remove the case statement within my query then I get the one row but I wouldn't know if I have a new message. I'm having trouble trying to get this query to return only 1 row and state 1 or 0 for NewMessage depending on if I have a new message or not.
Any help would be appreciated.
Following our conversation in the comments I think this will give you the result you are looking for:
Select p.Id [SenderId],
p.Username,
count(mr.RecipientId) [TotalMessages],
count(case when mr.ReadDate is null then 1 end) As [NewMessages],
up.PhotoId,
p.LastLoggedIn,
p.LoggedIn
FROM [User].[User_Profile] p
JOIN [MailBox].[Message] m on p.Id = m.SenderId
JOIN [MailBox].[MessageRecipient] mr on m.Id = mr.MessageId
LEFT JOIN [User].[User_Photos] up on p.Id = up.UserId and up.isProfilePic = 1
where mr.RecipientId = #UserId and mr.DeletedDate is null
GROUP BY p.id,
p.Username,
mr.RecipientId,
up.PhotoId,
p.LastLoggedIn,
p.LoggedIn
Related
thank you all for taking the time to read and help if you can! I have a query below that is getting large and messy, I was hoping someone could point me in the right direction as I am still a beginner.
SELECT
DATE(s.created_time_stamp) AS Date,
s.security_profile_id AS Name,
COUNT(*) AS logins,
CASE
WHEN COUNT(s.security_profile_id) <= 1
THEN '1'
WHEN COUNT(s.security_profile_id) BETWEEN 2 AND 3
THEN '2-3'
ELSE '4+'
END AS sessions_summary
FROM session AS s
INNER JOIN member AS m
ON s.security_profile_id = m.security_profile_id
JOIN member_entitlement AS me ON m.id = me.member_id
JOIN member_package AS mp ON me.id = mp.member_entitlement_id
**JOIN member_channels AS mc ON mc.member_id = m.id**
where member_status = 'ACTIVE'
and metrix_exempt = 0
and m.created_time_stamp >= STR_TO_DATE('03/08/2022', '%m/%d/%Y')
and display_name not like 'john%doe%'
and email not like '%#aeturnum.com'
and email not like '%#trendertag.com'
and email not like '%#sargentlabs.com'
and member_email_status = 'ACTIVE'
and mp.package_id = 'ca972458-bc43-4822-a311-2d18bad2be96'
and display_name IS NOT NULL
and s.security_profile_id IS NOT NULL
**and mc.id IS NOT NULL**
GROUP BY
DATE(created_time_stamp),
Name
ORDER BY
DATE(created_time_stamp),
Name
The two parts of the query with asterisks are the two most recently added clauses and they skew the data. Without these, the query runs fine. I am trying get a session summary which works fine, but I only want the sessions of people who have a 'channel' created. Maybe mc.id IS NOT NULL is not the way to do this. I will share my query that shows me how many people have created channels. Essentially, I am trying to combine these two queries in the cleanest way possible. Any advice is greatly appreciated!
-- Users that have Topic Channels and Finished Set Up FOR TRIAL DASH**
select count(distinct(m.id)) AS created_topic_channel
from member m right join member_channels mc on mc.member_id = m.id
left join channels c on c.id = mc.channels_id
JOIN member_entitlement AS me ON m.id = me.member_id
JOIN member_package AS mp ON me.id = mp.member_entitlement_id
where title not like '# Mentions'
and member_status = 'ACTIVE'
and metrix_exempt = 0
and m.created_time_stamp >= STR_TO_DATE('03/08/2022', '%m/%d/%Y')
and display_name not like 'john%doe%'
and email not like '%#aeturnum.com'
and email not like '%#trendertag.com'
and email not like '%#sargentlabs.com'
and member_email_status = 'ACTIVE'
and display_name IS NOT NULL
and mp.package_id = 'ca972458-bc43-4822-a311-2d18bad2be96';
The metric I am trying to retrieve from the DB is how many users have created a channel and logged in at least twice. Thank you again and have a wonderful day!!
If id is the primary key of member_channels then it does not make sense to check if it is null.
If all you want is to check whether a member has a 'channel' created, then instead of the additional join to member_channels, which may cause the query to return more rows than expected, you could use EXISTS in the WHERE clause:
where member_status = 'ACTIVE'
and .......................
and EXISTS (SELECT 1 FROM member_channels AS mc WHERE mc.member_id = m.id)
I would guess your tables aren't at the same level of granularity. A member may have many sessions, and 0-many channels.
eg if member 123 has five sessions and creates three channels => 15 rows of data in this join.
To adjust for this, it's best practice to join on the same level of granularity. You could roll up sessions to the member level, channels to the member level, and then join both against members.
I need to write a query which I think will need a subquery in it. Currently I'm writing the query as a raw SQL statement using DataGrip and will need to work on a postGres server. I am using Laravel to write the application in which this query needs to work in.
The two tables needed to write the query are media_files and statuses. There is a link between the 2 tables:
media_files.id = statuses.model_id
Files are stored in media_files and can have two statuses which are pending and attached. The statuses for files are stored in statuses. The statuses table can also contain statuses of other things such as tasks, events, users, etc.
I need a way of getting all the files where the last status for them is pending. Some files may not even have a pending status and these can be ignored.
The statuses table can hold multiple statuses of the same media file. So for example you can have:
Record 1
media_files.id = 1
media_files.name = 'CV document'
statuses.id = 2
statuses.model_id = 1
statuses.model_type = 'App\MediaFile'
statuses.name = 'attached'
statuses.created_at = '2020-06-16 17:39:08'
Record 2
media_files.id = 1
media_files.name = 'CV document'
statuses.id = 1
statuses.model_id = 1
statuses.model_type = 'App\MediaFile'
statuses.name = 'pending'
statuses.created_at = '2020-06-14 17:30:00'
I have made a start on the query but it doesn't seem to be working correctly:
select media_files.*, (
select name
from statuses
where model_id = media_files.id
and model_type = 'App\File'
order by statuses.created_at desc
limit 1
)
as latest_status
from media_files
inner join statuses on statuses.model_id = media_files.id
where statuses.model_type = 'App\Entities\Media\File'
order by media_files.id desc;
You can use conditional aggregation to determine if the last pending status is the last status:
select mf.*
from media_files mf join
(select s.model_id,
max(case when s.status = 'pending' then s.created_at end) as last_pending_created_at,
max(s.created_at) as last_created_at
from statuses s
group by s.model_id
) s
on s.model_id = mf.id
where last_pending_created_at = last_created_at;
You can also use a correlated subquery:
select mf.*
from (select mf.*,
(select s.status
from statuses s
where s.model_id = mf.id
order by s.created_at desc
limit 1
) as last_status
from media_files mf
) mf
where last_status = 'pending';
you can use an analytic function as well
SELECT MEDIA_FILES.*,LATEST_STATUS.*
FROM
(SELECT NAME,MODEL_ID,
MAX(CREATED_AT) OVER(PARTITION BY NAME) AS MAX_TM
WHERE MODEL_ID = MEDIA_FILES.ID
AND MODEL_TYPE = 'App\File') AS LATEST_STATUS,MEDIA_FILES
WHERE LATEST_STATUS.MODEL_ID=MEDIA_FILES.MODEL_ID
I am very new to Sql and I hope someone can help, I have a Table called Project_Table(pjtID) and another Table called Snag_Table(foreign key sngProject_ID), then i have a FeedBack_Table (foreign Key fdb_Snag_ID).
What i need is that when a user clicks on a snag they can write a message and when they return to the list of snags it must not show that they have any messages pending for them, but the user that the Snag was assigned to logs in and sees there is a message for them. How i handle whether it has been read or not is handled somewhere else. I have structured a SQL query that displays the list of snags and the counter.
SELECT COUNT(CASE WHEN fdbRead = 2 THEN 1 ELSE NULL END) AS ReadCount,
sngCreatedBy,sngAssignedToEmail,sngProject_ID,
u1.useFullName as Createdname,
sngCreatedBy,sngID,sngTitle,u2.useFullName as AssignedName,
sngAssignedTo,sngStatusName,sngDescription,sngStartDate,
sngDueDate,sngCreatedBy
FROM Snag_Table
INNER JOIN User_Table u1 ON Snag_Table.sngCreatedBy = u1.useID
Left JOIN User_Table u2 ON Snag_Table.sngAssignedTo = u2.useID
LEFT JOIN FeedBack_Table ON Snag_Table.sngID = fdb_Snag_ID
WHERE sngProject_ID = 10
AND sngIsActive = 1
GROUP BY sngCreatedBy,sngAssignedToEmail,sngProject_ID,
u1.useFullName,Snag_Table.sngID,Snag_Table.sngTitle,
u2.useFullName,sngAssignedTo,sngStatusName,sngDescription,
sngStartDate,sngDueDate
ORDER BY sngID DESC
What i am getting is the counter regardless of who is logged in and who created the feedback.
So i tried adding some logic into the query to display counter based on who is logged in so i added after the where fdbCreatedBy != 17
SELECT COUNT(CASE WHEN fdbRead = 2 THEN 1 ELSE NULL END) AS ReadCount,
sngCreatedBy,sngAssignedToEmail,sngProject_ID,
u1.useFullName as Createdname,sngCreatedBy,
sngID,sngTitle,u2.useFullName as AssignedName,
sngAssignedTo,sngStatusName,sngDescription,sngStartDate,
sngDueDate,sngCreatedBy
FROM Snag_Table
INNER JOIN User_Table u1 ON Snag_Table.sngCreatedBy = u1.useID
Left JOIN User_Table u2 ON Snag_Table.sngAssignedTo = u2.useID
LEFT JOIN FeedBack_Table ON Snag_Table.sngID = fdb_Snag_ID
WHERE *fdbCreatedBy != 17*
AND sngProject_ID = 10
AND sngIsActive = 1
GROUP BY sngCreatedBy,sngAssignedToEmail,sngProject_ID,
u1.useFullName,Snag_Table.sngID,Snag_Table.sngTitle,
u2.useFullName,sngAssignedTo,sngStatusName,sngDescription,
sngStartDate,sngDueDate
ORDER BY sngID DESC
Now the counter does work as desired but only if there are already feedbacks attached to the snag, but if i create a new snag an refresh the list it doesnt show the snag.
Any help would be appreciated.
This may help you
Remove the condition from where
WHERE *fdbCreatedBy != 17*
and add that condition in your case statement
Count(CASE WHEN (fdbread = 2 and [fdbcreatedby] != 17 )THEN 1 ELSE NULL END) AS readcount
I've got a query I'm trying to write to get counts of active users and active contacts associated with each account. I have attempted to run the counts separately and in both cases they run at under 1 sec but when I put them together as seen below I don't get a result. Please let me know if there is anything I can do it enhance the query.
select count(c.c_no) as contacts_count, count(u_no) as user_count, a.*
from accounts a
LEFT JOIN users u on u.a_no = a.a_no and u_status = 1
LEFT JOIN IDP1.contacts c on c.a_no = a.a_no and c_status = 1
where a_status = 1
group by a_no
THANK YOU!
Do not use * and aggregate functions simultaneously
Try this
select count(c.c_no) as contacts_count, count(u_no) as user_count, a_no
from accounts a
LEFT JOIN users u on u.a_no = a.a_no and u_status = 1
LEFT JOIN IDP1.contacts c on c.a_no = a.a_no and c_status = 1
where a_status = 1
group by a_no
I have 4 tables user, posts, follows, notifications. The posts table has a field called privacy in which a value of 1 means "anyone can see" and 2 means "only friends can see".
My notifications table looks like this:
id user_id tonotify_id notification post_id
1 2 3 --- 1
2 3 2 --- 2
3 2 4 --- 3
And my follows table looks like this:
id user_id tofollow_id status fstatus
1 1 2 1 1
2 1 3 1 1
The field fstatus value of 1 means that they are also friends.
Now I am trying to get all notifications sent out by people who a certain user is following (lets say a user with id 1). Assuming 1 is following user 2 and 3, I need to get all notifications from the above table. However, before this I need to make sure that the post (posted by the users in the tonotify_id column) is available. That is, I need to check the privacy setting. I have the following code:
SELECT
u.username AS sender,
ux.username AS receiver,
p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
WHERE f.user_id = 1
AND fr.user_id = 1
AND f.status = 1
AND p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1)
ORDER BY n.id DESC
It seems to be working except for one glitch. Since user 1 is not following user 4 all notifications aimed at 4, even if the posts are public don't show up. This my guess has to do with JOIN follows fr ON (n.tonotify_id = fr.tofollow_id) because, since the user 1 hasn't followed 4, there are no rows matching this join. Any suggestions to solving this?
I did try [the outer join], but the output is the same.
When you use an outer join, and then use one of the "outer" columns in an equality check in the WHERE clause, you convert your outer join to an inner join. This is because your condition that checks post's privacy requires the post to be there:
AND p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1)
When an outer join is about to produce a row that corresponds to a notification without a post, it would check the above condition. Since the post is not there, p.privacy would evaluate to NULL, "contaminating" both sides of the OR, and eventually making the whole condition evaluate to false.
Moving this condition into the ON condition of the join will fix the problem:
SELECT
u.username AS sender,
ux.username AS receiver,
p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
AND (p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1))
WHERE f.user_id = 1
AND fr.user_id = 1
AND f.status = 1
ORDER BY n.id DESC
Another way to fix this would be adding an IS NULL condition to your OR, like this:
SELECT
u.username AS sender,
ux.username AS receiver,
p.id
FROM notifications n
JOIN follows f ON (n.user_id = f.tofollow_id)
JOIN follows fr ON (n.tonotify_id = fr.tofollow_id)
JOIN user u ON (u.id = n.user_id)
JOIN user ux ON (ux.id = n.tonotify_id)
LEFT JOIN posts p ON (n.posts_id = p.id)
WHERE f.user_id = 1
AND fr.user_id = 1
AND f.status = 1
AND (p.privacy IS NULL OR p.privacy = 1 OR (p.privacy = 2 AND fr.fstatus = 1))
ORDER BY n.id DESC
You need to use an outer join such as LEFT JOIN instead of an inner join using just JOIN. An outer join will always return rows from one "side" (which is why it is called LEFT [OUTER] JOIN and RIGHT [OUTER] JOIN) even if the other "side" does not match anything.