How can I show when users were last active based on statistics? - sql

I have a database that stores the datetime from getdate() when they log into the system.
I want to do a select that shows ones that have been active in the last 10mins as 'active now' and for all the rest, say how long ago they were active e.g. 3 days or 3hrs.
I have no idea how to do the second part of that.
This is what I have done to get the active users:
SELECT DISTINCT FirstName, LastName, 'Active now'
FROM dbo.Users UP
INNER JOIN Portal.dbo.UserStatistics US
ON US.UserID = UP.UserID
INNER JOIN Portal.dbo.Pages
ON pages.id = US.PageID
WHERE US.DateTimeLastUpdated >= dateadd(minute, -5, getdate())

SELECT DISTINCT FirstName, LastName,
CASE when US.DateTimeLastUpdated >= dateadd(minute, -5, getdate()) then 'Active now' else cast(datediff(h,getdate(), US.DateTimeLastUpdated) as varchar) end
FROM dbo.Users UP
INNER JOIN Portal.dbo.UserStatistics US
ON US.UserID = UP.UserID
INNER JOIN Portal.dbo.Pages
ON pages.id = US.PageID

You can use a case statement with a group by clause:
SELECT
FirstName,
LastName,
case when max(US.DateTimeLastUpdated) >= dateadd(minute, -5, getdate()) then
'Active now'
when max(US.DateTimeLastUpdated) >= dateadd(day, -2, getdate()) then
'Active 3 days'
...
end as [last viewed]
FROM
dbo.Users UP
INNER JOIN
Portal.dbo.UserStatistics US
ON US.UserID = UP.UserID
INNER JOIN
Portal.dbo.Pages
ON pages.id = US.PageID
GROUP BY
FirstName,
LastName

You need to aggregate to get the latest access per user, and then work with that, so something a little like:
SELECT up.FirstName,
up.LastName,
CASE
WHEN MAX(US.DateTimeLastUpdated) >= dateadd(minute, -5, getdate()) THEN 'Now'
ELSE CAST(MAX(DATEDIFF(H, US.DateTimeLastUpdated, GETDATE())) AS VARCHAR) + ' hours ago'
AS LastActive
FROM dbo.Users UP
INNER JOIN Portal.dbo.UserStatistics US
ON US.UserID = UP.UserID
INNER JOIN Portal.dbo.Pages
ON pages.id = US.PageID
GROUP BY up.FirstName,
up.LastName

Related

Convert nested Query to Join in SQL Server

I have a query
SELECT *
FROM Stops
WHERE CustomerID IN (SELECT ID FROM Customers WHERE Active = 1)
AND DriverID IS NOT NULL
AND TripID IN (SELECT ID
FROM Trips
WHERE ManagerID IN (SELECT ID FROM Users WHERE Active = 1)
AND AssignedToID IN (SELECT ID FROM Users WHERE Active = 1)
AND Modified > DATEADD(day, -60, GETDATE()))
I tried to convert to Join but I am stuck
SELECT *
FROM Stops S
JOIN Customers C ON C.ID = S.CustomerID
JOIN Trips T ON S.TripID = T.ID
WHERE C.ACTIVE = 1
AND S.DriverID IS NOT NULL
AND T.Modified > DATEADD(day, -60, GETDATE())
Using all joins, no nested queries
SELECT * FROM Stops A
INNER JOIN Customers B ON A.CustomerID = B.ID
INNER JOIN Trips C ON A.TripID = C.ID
INNER JOIN Users D ON C.ManagerID = D.ID
INNER JOIN Users E ON C.AssignedToID = E.ID
WHERE A.DriverID IS NOT NULL AND
B.Active = 1 AND
D.Active = 1 AND
E.Active = 1 AND
C.Modified > DATEADD(day, -60, GETDATE());
If you want unique data of stops you can also add "DISTINCT" to the select.
you can try like below subquery and join
SELECT S.* FROM Stops S
JOIN Customers C ON C.ID=S.CustomerID
join (SELECT ID FROM Trips where
ManagerID IN (SELECT ID FROM Users WHERE Active = 1) AND
AssignedToID IN (SELECT ID FROM Users WHERE Active = 1) AND
Modified > DATEADD(day, -60, GETDATE())
) t on S.TripID=t.ID
I'm trying your second code on my end until I came up on the below code. You might try
SELECT *
FROM Stops S
JOIN Customers C ON C.ID = S.CustomerID AND C.ACTIVE = 1
JOIN Trips T ON S.TripID = T.ID AND T.Modified > DATEADD(day, -60, GETDATE())
LEFT JOIN Users U ON T.ManagerID = U.ID AND T.AssignedToID = U.ID
WHERE S.DriverID IS NOT NULL
What I usually do is to draw squares as tables and link them based on the requirements.
Though, still not sure if my answer would work since I have no idea with what you are trying to achieve on your code aside from using JOIN.

Confusing sum result

I've 3 tables as below.
Users
StatusTable
Time_Tracker
And the relation is as below.
users have username and userid, StatusTable has userName and Time_Tracker has userName
Below is my table Data.
And here I'm trying to get the sum
Problem:
I want to update the productiontime as sum of time taken[minutes] by joining case owner to username and TT.userId = Users.userId and the login = GetDate()
My Query is
update Time_Tracker set ProductionTime = (select sum(ST.[time taken(minutes)])
from statustable as st inner join users as u
on st.[case owner] = u.username
inner join Time_Tracker as TT
on u.userId = TT.userId
where cast(st.[Start Time] AS DATE) = CAST(GETDATE() as Date)
Group By TT.UserId, u.UserId) where CAST(Login AS DATE) = CAST(GETDATE() as Date)
And the blue box in my image is my current O/p.
When I run
select sum([time taken(minutes)]) as totalTme from StatusTable where cast([Start Time] AS DATE) = CAST(GETDATE() as Date)
I get the O/p as 2.05 which is correct one.
please let me know where am I going wrong in my first query(printing 10.25) instead of 2.05.
How can I fix this.
Thanks
I think you have too many joins in the subquery:
update Time_Tracker tt
set ProductionTime = (select sum(ST.[time taken(minutes)])
from statustable st inner join
users u
on st.[case owner] = u.username
where u.userId = tt.userId and
cast(st.[Start Time] AS DATE) = CAST(GETDATE() as Date)
)
where CAST(Login AS DATE) = CAST(GETDATE() as Date);
Your version is multiplying the result for each row in TimeTracker. The correlation clause is what you need.
Also very important. Such a correlated subquery should not have a GROUP BY clause. The GROUP BY could return multiple rows, but by definition the correlated subquery should return at most one row.

Select ID from customers that do not have contacts last 7 days and 2 times in the month

I have 2 tables: customer, c_contact.
c_contact is all the e-mails I send to my customers.
From now on I need to put a new rule that the customer can't receive a new e-mail if:
1) It received an e-mail in the last 7 days
2) Have 2 or more e-mails sent in the current month
I thought something like that:
SELECT * from customer c inner join c_contact cc on cc.ID = c.ID WHERE
ID not in (select ID from c_contact c1 where c1.ID = cc.ID and
c1.CONTDATE >= getdate()-7) AND
ID not in (select count(ID) from c_contact where MONTH(contdate) = MONTH(getdate())
and YEAR(contdate) = YEAR(getdate() HAVING count(ID) >= 2)
But the table c_contact is huge and it taking ages to run this.
Is there a way to do these 2 conditions in 1 "ID not in"? I think it will run a lot faster.
I'm not sure how much better this would be performance wise, but off the top of my head you could do this.
SELECT *
FROM customer c
OUTER APPLY (SELECT COUNT(*) monthCount
FROM c_contact cc
WHERE cc.ID = c.ID
AND cc.contdate >= DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0)
AND cc.contdate < DATEADD(mm, DATEDIFF(mm, 0, GETDATE()) + 1, 0)) ct
OUTER APPLY (SELECT MAX(cc.contdate) lastSent
FROM c_contact cc
WHERE cc.ID = c.ID AND cc.contdate < GETDATE()) ls
WHERE ct.monthCount < 2
AND ls.lastSent < DATEADD(dd, -7, GETDATE())
Or, using a left join instead of 2 outer applies, you can try:
SELECT *
FROM customer c
LEFT JOIN (
SELECT cc.ID,
COUNT(CASE WHEN MONTH(cc.contdate) = MONTH(GETDATE()) THEN 1 END) monthCount,
MAX(cc.contdate) lastSent
FROM c_contact cc
WHERE cc.contdate BETWEEN DATEADD(dd, -32, GETDATE()) AND GETDATE()
GROUP BY cc.ID
) cc ON c.ID = cc.ID
WHERE ISNULL(cc.monthCount,0) < 2
AND ISNULL(cc.lastSent,GETDATE()) < DATEADD(dd, -7, GETDATE())
if you really just want to use NOT IN you can try:
SELECT *
FROM customer c
WHERE c.ID NOT IN (
SELECT cc.ID,
COUNT(CASE WHEN MONTH(cc.contdate) = MONTH(GETDATE()) THEN 1 END) monthCount,
MAX(cc.contdate) lastSent
FROM c_contact cc
WHERE cc.contdate BETWEEN DATEADD(dd, -32, GETDATE()) AND GETDATE()
GROUP BY cc.ID
HAVING COUNT(CASE WHEN MONTH(cc.contdate) = MONTH(GETDATE()) THEN 1 END) > 1
AND MAX(cc.contdate) > DATEADD(dd, -7, GETDATE())
)
This can also be done in other way:
select A.* from Customers A
where A.CustID not in (
select B.CustID from (
(select C.CustID, Count(C.CustID) cnt from Contacts C
where C.ContactedDate >=GETDATE()-7 group by C.CustID)
UNION
(select C.CustID, Count(C.CustID) cnt from Contacts C
where C.ContactedDate <=GETDATE()-7 and Month(C.ContactedDate) = Month(GETDATE()) and YEAR(C.ContactedDate)= YEAR(GETDATE())
group by C.CustID having COUNT(C.CustID) >= 2)) B);
go
Divide customer IDs into two sets- the first set containing IDs that were contacted in the recent 7 days, and the second set containing IDs that were contacted in the last three weeks of the current month. Take UNION of these two sets to finally exclude while selecting the desired set of customers.

ms sql query help. subquery returned more than 1 value

Here is my query:
SELECT
Bills.BillDate,
Client.ClientName,
(SELECT bills.NetAmount
FROM Bills
Where Bills.BillDate Between DATEADD(day, -30, GETDATE()) AND GETDATE()) as '30 days'
FROM Client INNER JOIN
Vessel ON Client.ClientID = Vessel.ClientID INNER JOIN
Bills ON Vessel.VesselID = Bills.VesselID
GROUP BY Bills.BillDate, Client.ClientName
Use this:
SELECT
Bills.BillDate,
Client.ClientName,
(
SELECT TOP 1 bills.NetAmount
FROM Bills
WHERE
Bills.BillDate BETWEEN DATEADD(day, -30, GETDATE()) AND GETDATE()
) AS '30 days'
FROM Client
INNER JOIN Vessel
ON Client.ClientID = Vessel.ClientID
INNER JOIN Bills
ON Vessel.VesselID = Bills.VesselID
GROUP BY
Bills.BillDate, Client.ClientName
TOP 1 will get only 1 result from the inner query.
Adjusted to SUM the bills.NetAmount for the last 30 days:
SELECT
Bills.BillDate,
Client.ClientName,
SUM(bills.NetAmount) as '30 days'
FROM Client INNER JOIN
Vessel ON Client.ClientID = Vessel.ClientID INNER JOIN
Bills ON Vessel.VesselID = Bills.VesselID
WHERE Bills.BillDate BETWEEN DATEADD(day, -30, GETDATE()) AND GETDATE()
GROUP BY Bills.BillDate, Client.ClientName

If field = Null then use a different field

I have written some Sql code to display all clients who's offers are about to expire in the next 90 days by using the dateOffered field. However there is another field in the database called OfferExpirydate I would use this field however it it not always filled out.
My question is i want the code to look at OfferExpirydate and if it has a value then use it or else use the Dateoffered field as my code below stats.
( if the OfferExpirydate is not filled out it is set to a NULL )
Any help on this would be great thanks
SELECT
DateOffered,
pr.ClientID,
pr.id AS profileID,
cf.Clntnme,
pm.Lender,
ABS(DATEDIFF(DAY, DateOffered, DATEADD(d,-90, GETDATE()))) AS 'NoOfDays'
FROM tbl_profile AS pr
INNER JOIN tbl_Profile_Mortgage AS pm
ON pr.id = pm.fk_profileID
INNER JOIN dbo.tbl_ClientFile AS cf
ON pr.ClientID = cf.ClientID
WHERE
DateCompleted IS NULL AND
DateOffered > DATEADD(d,-90, GETDATE())
AND DATEDIFF(DAY, DateOffered, DATEADD(d,-90, GETDATE())) > -15
ORDER BY DateOffered ASC
COALESCE(col1, col2, ...)
will pick the first non-null value.
Try this:
SELECT DateOffered,
pr.ClientID,
pr.id AS profileID,
cf.Clntnme,
pm.Lender,
ABS(DATEDIFF(DAY, DateOffered, DATEADD(d,-90, GETDATE()))) AS 'NoOfDays'
FROM tbl_profile AS pr
INNER JOIN tbl_Profile_Mortgage AS pm
ON pr.id = pm.fk_profileID
INNER JOIN dbo.tbl_ClientFile AS cf
ON pr.ClientID = cf.ClientID
WHERE DateCompleted IS NULL AND
1 = CASE WHEN OfferExpirydate IS NOT NULL AND DATEDIFF(DAY, OfferExpirydate, GETDATE()) > -15 THEN 1
WHEN DateOffered > DATEADD(d,-90, GETDATE()) AND DATEDIFF(DAY, DateOffered, DATEADD(d,-90, GETDATE())) > -15 THEN 1
ELSE 0
END
ORDER BY DateOffered ASC