need help to group my result - sql-server

I am using sql server 2008 r2. here's my query.
SELECT TOP 25 A.*, U.Displayname AS UserName,
SU.Displayname AS SmoothieAuthorName,
S.Name AS SmoothieName, S.Id AS SmoothieId
FROM dbo.Activity AS A
LEFT JOIN dbo.[User] AS U ON A.UserId = U.Id
LEFT JOIN dbo.[User] AS SU ON A.SmoothieAuthorId = SU.Id
LEFT JOIN dbo.Smoothie AS S ON A.SmoothieId = S.Id
WHERE A.UserId = 2 --#UserId
AND A.UserId <> A.SmoothieAuthorId
ORDER BY CreatedDate DESC
following is my result. now, i need to group them by SmoothieId and CreatedDate (only group date part, ignore time). First two should only return one back, 3 to 5 should only return one back. not sure how to do it, please help.
Id ActionType UserId SmoothieId SmoothieAuthorId CreatedDate UserName SmoothieAuthorName SmoothieName SmoothieId
1 view 2 128 1 2013-01-15 20:05:03.403 mike test1234 new testing 2d 128
2 view 2 128 1 2013-01-15 20:16:24.733 mike test1234 new testing 2d 128
12 view 2 128 1 2013-01-16 21:45:56.167 mike test1234 new testing 2d 128
13 view 2 128 1 2013-01-16 22:12:51.217 mike test1234 new testing 2d 128
14 view 2 128 1 2013-01-16 22:12:54.407 mike test1234 new testing 2d 128
15 view 2 69 1 2013-01-16 22:19:54.783 mike test1234 sdfsdfwww 69

If you need ALL columns from A including Id, I think you'll have a have a hard time including Id. I think you'll need to explicitly list the columns from A you are after.
I've also assumed you want a count of records you are grouping, hence the Count(TheDate) element.
Other than that, look at getting just the date portion of a datetime and group on that.
Something like;
SELECT ActionType, UserId, SmoothieId, SmoothieAuthorId,
TheDate, Count(TheDate) AS Occurances, UserName, SmoothieAuthorName,
SmoothieName, SmoothieId
FROM (
SELECT TOP 25 A.ActionType, A.UserId, A.SmoothieId,
A.SmoothieAuthorId,
DATEADD(dd, 0, DATEDIFF(dd, 0, CreatedDate)) AS TheDate,
U.Displayname AS UserName,
SU.Displayname AS SmoothieAuthorName,
S.Name AS SmoothieName, S.Id AS SmoothieId
FROM dbo.Activity AS A
LEFT JOIN dbo.[User] AS U ON A.UserId = U.Id
LEFT JOIN dbo.[User] AS SU ON A.SmoothieAuthorId = SU.Id
LEFT JOIN dbo.Smoothie AS S ON A.SmoothieId = S.Id
WHERE A.UserId = 2 --#UserId
AND A.UserId <> A.SmoothieAuthorId
-- ORDER BY CreatedDate DESC
) x GROUP BY ActionType, UserId, SmoothieId, SmoothieAuthorId, UserName,
TheDate, SmoothieAuthorName, SmoothieName, SmoothieId
ORDER BY The Date DESC
Note This isn't tested, it is just a quick suggestion at what I'd try.

I'm not sure I fully understand the question. Assuming you want distinct values for those columns, then just use DISTINCT:
SELECT DISTINCT
CAST(CreatedDate to Date) as DateWanted,
SmoothieId
FROM
(
SELECT TOP 25 A.*, U.Displayname AS UserName,
SU.Displayname AS SmoothieAuthorName,
S.Name AS SmoothieName, S.Id AS SmoothieId
FROM dbo.Activity AS A
LEFT JOIN dbo.[User] AS U ON A.UserId = U.Id
LEFT JOIN dbo.[User] AS SU ON A.SmoothieAuthorId = SU.Id
LEFT JOIN dbo.Smoothie AS S ON A.SmoothieId = S.Id
WHERE A.UserId = 2 --#UserId
AND A.UserId <> A.SmoothieAuthorId
) t
Reviewing your comment, you say you need all fields in the Activity table. What do you expect to receive in your Id column for the first 2 records, 1 or 2? Are all the other values in the other columns the same? To just get a single record, you're going to need to either pick one row over the other, or do a group by with the columns that have the same information.
Good luck.

Related

Create a stored procedure to aggregate rows

Having a transaction table with the following rows:
Id UserId PlatformId TransactionTypeId
-------------------------------------------------
0 1 3 1
1 1 1 2
2 2 3 2
3 3 2 1
4 2 3 1
How do I write a stored procedure that can aggregate the rows into a new table with the following format?
Id UserId Platforms TransactionTypeId
-------------------------------------------------
0 1 {"p3":1,"p1":1} {"t1":1,"t2":1}
1 2 {"p3":2} {"t2":1,"t1":1}
3 3 {"p2":1} {"t1":1}
So the rows are gouped by User, count each platform/transactionType and store as key/value json string.
Ref: My previous related question
You could use GROUP BY and FOR JSON:
SELECT MIN(ID) AS ID, UserId, MIN(sub.x) AS Platforms, MIN(sub2.x) AS Transactions
FROM tab t
OUTER APPLY (SELECT CONCAT('p', platformId) AS platform, cnt
FROM (SELECT PlatformId, COUNT(*) AS cnt
FROM tab t2 WHERE t2.UserId = t.UserId
GROUP BY PlatformId) s
FOR JSON AUTO) sub(x)
OUTER APPLY (SELECT CONCAT('t', TransactiontypeId) AS Transactions, cnt
FROM (SELECT TransactiontypeId, COUNT(*) AS cnt
FROM tab t2 WHERE t2.UserId = t.UserId
GROUP BY TransactiontypeId) s
FOR JSON AUTO) sub2(x)
GROUP BY UserId;
DBFiddle Demo
Result is a bit different(array of key-value) but please treat it as starting point.
Your sample JSON is not really a json, but since you want it that way:
SELECT u.UserId, plt.pValue, ttyp.ttValue
FROM Users AS [u]
CROSS APPLY (
SELECT '{'+STUFF( (SELECT ',"'+pn.pName+'":'+LTRIM(STR(pn.pCount))
FROM (SELECT p.Name AS pName, COUNT(*) AS pCount
FROM transactions t
left JOIN Platforms p ON p.PlatformID = t.PlatformId
WHERE t.UserId = u.UserId
GROUP BY p.PlatformId, p.Name
) pn
FOR XML PATH('')),1,1,'')+'}'
) plt(pValue)
CROSS APPLY (
SELECT '{'+STUFF( (SELECT ',"'+tty.ttName+'":'+LTRIM(STR(tty.ttCount))
FROM (SELECT tt.Name AS ttName, COUNT(*) AS ttCount
FROM transactions t
left JOIN dbo.TransactionType tt ON tt.TransactionTypeId = t.TransactionTypeID
WHERE t.UserId = u.UserId
GROUP BY tt.TransactionTypeId, tt.Name
) tty
FOR XML PATH('')),1,1,'')+'}'
) ttyp(ttValue)
WHERE EXISTS (SELECT * FROM transactions t WHERE u.UserId = t.UserId)
ORDER BY UserId;
DBFiddle Sample

Using RowNumber and Partition

Consider this code:
Select U.[user_id] As UserID
Max(AL.entry_dt) As LastLoginDate
From Users U with (nolock)
Inner Join activity_log AL with (nolock) On AL.[user_id] = U.[user_id]
And AL.activity_type = 'LOGIN'
And U.external_user = 1
Group By U.[user_id]
Having Max(al.entry_dt) < GetDate() - 30
Order By U.[user_id]
I was curious if the Row_Number / Partition could be used here? Perhaps to make this more effective, or if it can be used at all?
Essentially, I want 1 row per user with the last instance the user logged in where the user hasn't logged in during the last 30 days.
Bring on the pain.....
To use the result of the row_number() in a where clause, wrap the query in a subquery/derived table or common table expression:
Original answer for users that have logged in within the last 30 days:
select UserId, LastLoginDate
from (
Select
U.[user_id] As UserID
, AL.entry_dt As LastLoginDate
, rn = row_number() over(partition by u.user_id order by AL.entry_dt desc)
From Users U with (nolock)
Inner Join activity_log AL with (nolock)
On AL.[user_id] = U.[user_id]
And AL.activity_type = 'LOGIN'
And U.external_user = 1
Where AL.entry_dt > GetDate() - 30 -- swapped < for >
) sub
where rn = 1
Order By sub.[userid]
rextester demo: http://rextester.com/XZU40394
returns:
+--------+---------------+
| UserId | LastLoginDate |
+--------+---------------+
| 1 | 2017-09-13 |
| 2 | 2017-09-10 |
| 3 | 2017-09-07 |
+--------+---------------+
Updated answer for users who have not logged in in the last 30 days:
select UserId, LastLoginDate
from (
Select
U.[user_id] As UserID
, AL.entry_dt As LastLoginDate
, rn = row_number() over(partition by u.user_id order by AL.entry_dt desc)
From Users U with (nolock)
Inner Join activity_log AL with (nolock)
On AL.[user_id] = U.[user_id]
And AL.activity_type = 'LOGIN'
And U.external_user = 1
) sub
where rn = 1
and lastlogindate < getdate() - 30
Order By [userid]
rextester demo: http://rextester.com/XZU40394
returns:
+--------+---------------+
| UserId | LastLoginDate |
+--------+---------------+
| 4 | 2016-09-13 |
| 6 | 2016-09-10 |
+--------+---------------+
from test setup:
create table users (user_id int, external_user bit)
create table activity_log (user_id int, activity_type varchar(32), entry_dt date)
insert into users values (1,1),(2,1),(3,1),(4,1),(5,0),(6,1)
insert into activity_log values
(1,'login','20170913') ,(1,'login','20170912') ,(1,'login','20170911'),(1,'login','20160908')
,(2,'login','20170910') ,(2,'login','20170909') ,(2,'login','20170908')
,(3,'login','20170907') ,(3,'login','20170906') ,(3,'login','20170905')
,(4,'login','20160913') ,(4,'login','20160912') ,(4,'login','20160908')
,(5,'login','20160910') ,(5,'login','20160909') ,(5,'login','20160908')
,(6,'login','20160910') ,(6,'login','20160909') ,(6,'login','20160908')
To correct your query in the question, move your where to having like so:
Select U.[user_id] As UserID
,Max(AL.entry_dt) As LastLoginDate
From Users U with (nolock)
Inner Join activity_log AL with (nolock) On AL.[user_id] = U.[user_id]
And AL.activity_type = 'LOGIN'
And U.external_user = 1
Group By U.[user_id]
having max(al.entry_dt) < GetDate() - 30
Order By U.[user_id]
CROSS APPLY or OUTER APPLY allow you to return n records from correlated query for each record in related table. I think cross apply is what you want since if a user hasn't logged in in the past 30 days you don't want to see them at all in results. Cross apply similar to inner join but runs correlation query for each record related table. OUTER Apply similar to OUTER join so it returns all records from related table and only those that match in the correlated query.
So in the below example, for each user, return the top 1 record in descending order of entry_dT. for each related user. Outer apply would resemble a left join so all users would be returned even if no activity occurred.
MODIFIED DEMO: http://rextester.com/UQEI69366 (all 3 below) again thx to SQLZim for tester/data
SELECT U.[user_id] As UserID
, AL.entry_dt As LastLoginDate
FROM Users U with (nolock)
CROSS APPLY (SELECT top 1 *
FROM activity_log IAL
WHERE U.User_ID = IAL.User_ID
AND IAL.activity_type = 'LOGIN'
ORDER BY IAL.entry_DT Desc) AL
WHERE U.external_user = 1
AND IAL.entry_dt < GetDate() - 30
ORDER BY U.[user_id]
If all you're after is users who haven't logged in in the past 30 days...
a simple not exists seems like it would work. Who cares about the date time if they have; you're just after a list of users who haven't logged in in 30 days.
SELECT U.[user_id] As UserID
FROM Users U
WHERE not exists (SELECT *
FROM activity_log IAL
WHERE IAL.activity_type = 'LOGIN'
AND IAL.entry_dt > GetDate() - 30
AND IAL.[user_id] = U.[user_id])
AND U.external_user = 1
ORDER BY U.[user_id]
a simple left join would work as well (return all external users who have not had a login in 30 days from present date.
SELECT U.[user_id] As UserID
FROM Users U with (nolock)
LEFT JOIN activity_log AL
ON AL.[user_id] = U.[user_id]
AND AL.activity_type = 'LOGIN'
AND AL.entry_dt > GetDate() - 30
WHERE U.external_user = 1
and AL.user_ID is null
ORDER BY U.[user_id]
I was curious if the Row_Number / Partition could be used here? Perhaps to make this more effective, or if it can be used at all?
I prefer group by in your case than row number since row number needs additional index than group by.Read below to know more
Assuming you use the same query you posted,below are the indexes needed
for users table..
create index nci_test on
dbo.usertable(userid,external_login)
For activity log table, you will need to know more about the data..
Ex:
if join filters out more rows than where,then index can be
create index nci_test1 on
dbo.actvititlog(userid,entry_Dt,activity_type )
if entry_dt column filters out more rows,then leading column can be entry_Dt in above index
if you use RowNumber,it will need a POC index and your query spreads across two tables,so this can't be done

create sql query to fetch repeat column values within time frame

Can someone help me with this query? I want to get the result of all the customer_id which repeats more than once in 24hrs
SELECT
O.Order_No, O.Customer_ID, O.DateOrdered, O.IPAddress,
C.FirstName, C.LastName, CD.nameoncard
FROM
Order_No O
INNER JOIN
CardData CD ON O.card_id = CD.id
INNER JOIN
Customers C ON O.customer_id = C.customer_id
ORDER BY
O.order_no desc
adding more details..
so suppose order with customer id xx was placed on 04/23 2:30 pm and again 2nd order was placed with same customer Id xx on same day 04/23 5:30 pm.
i want the query to return me customer Id xx
Thanks
select Customer_ID, CAST(DateOrdered as Date) DateOrdered, count(*) QTDE
from Order_No
group by Customer_ID, CAST(DateOrdered as Date)
having count(*) > 1
To get the customers who have orders issued after the first one, then you could use the following query:
select distinct A.Customer_ID
from Order_No A
inner join (select Customer_ID, min(DateOrdered) DateOrdered from Order_No group by Customer_ID ) B
on A.Customer_ID = B.Customer_ID
and A.DateOrdered - B.DateOrdered <= 1
and A.DateOrdered > B.DateOrdered
SQL Fiddle
To get all customers that have ANY TIME more than one order issued in period less or equal than 24h
select distinct A.Customer_ID
from Order_No A
inner join Order_No B
on A.Customer_ID = B.Customer_ID
and A.DateOrdered > B.DateOrdered
and A.DateOrdered - B.DateOrdered <= 1
SQL Fiddle
Self-join:
SELECT distinct O.Customer_ID
FROM
Order_No O
inner join Order_No o2
on o.customerID = o2.customerID
and datediff(hour, o.DateOrdered, o2.DateOrdered) between 0 and 24
and o.Order_No <> o2.Order_No
This will return all customer_IDs that have ever placed more than one order in any 24 hour period.
Edited to add the join criteria that the matching records should not be the same record. Should return customers who placed two different orders at the same time, but not customers who placed only one order.

How to get values from two tables in SQL Server?

I have 2 tables in SQL Server 2008 and I want to get the details from those 2 tables using join.
T-1 : vwHardwareConsolidate
|ID|||Qty|Type|Task_Id|
T-2 :
|MasterID|Task_Id|Act_Qty|
I want to get id, task_name, sum(qty), task_id from T1 and Masterid, Act_Qty from T2
I have tried this query
select
ID as MasterID, Task_id, Task_Name as Items,
SUM(Qty) as Req_Qty, 0 as Act_Qty
from
vwHardwareConsolidate
where
type = 'Reqrd' and ID = '21'
Group by
Task_Name,id,Task_id
union
(select
m.MasterID, m.Task_Id, vw.Task_Name as Items, 0 as Req_Qty, m.Act_Qty
from
vwHardwareConsolidate vw
Right join
(select
MasterID, m.Task_Id, 0 as Req_Qty, sum(Act_qty) as Act_Qty
from
tbl_MaterialDistribution_Detail m
where
MasterID = '21'
group by
m.Task_Id, MasterID) as m on m.Task_Id = vw.Task_id)
vwHardwareConsolidate
ID Site_name Qty Task_Name Type
1 CITY 1 A16Port_Switch Reqrd
1 CITY 1 Digital_Camera Reqrd
1 CITY 1 Electronic_Pen Reqrd
tbl_MaterialDistribution_Detail:
MasterID|TaskId|Act_qty
7 31 1
2 32 1
12 39 3
Please try this
select t1.ID, t1.Task_Name,Sum(t1.Qty) as Qty,t1.Task_Id,t2.MasterID,t2.Act_Qty
from vwHardwareConsolidate as t1
left outer join table2 as t2
on t2.Task_ID=t1.Task_ID
Group By t1.ID, t1.Site_name, t1.Task_Name,t1.Qty,t1.Type,t1.Task_Id,t2.MasterID,t2.Act_Qty
May this will help you.
SQL Fiddle Demo
You can use
SELECT T1.Id, T1.SiteName,T1.TaskName,T.Type,T2.Act_Qty,T2.MasterID,T2.Task_Id,SUM(Qty)AS
Qty FROM T1 INNER JOIN T2 ON T1.Task_Id=T2=Task_Id GROUP BY T1.Id, T1.SiteName,
T1.TaskName,T.Type,T2.Act_Qty,T2.MasterID,T2.Task_Id
something this way,
select a.id,a.Task_Name,b.qty,c.Act_Qty from vwHardwareConsolidate a inner join
tbl_MaterialDistribution_Detail c on a.id=c.task_id
inner join
(slect id,sum(qty)qty from vwHardwareConsolidate group by id)b
on a.id=b.id

one user many phones in a temp table

I need to show in a temporary table one user a many phone of that user, but I'm stuck
in the select, I need something like this:
user1 phone1 phone2 phone3 phone4 phone5
11816116-5 8555588 77877888 33254477 224474 45777885
this is the code that I'm trying:
select
phone As phonenum
Into #Tmp_phonenumber
From
clients_has_phones
where
user_number='11816116-5'
thanks in advance.
I can not think of a good way of doing the select statement other than by self joining on how ever many phone numbers your user may have.. With that being said you can try this for your select statement:
;With CTE_Main as (
Select
id
,Fono
,row_number()
Over(Partition by ID order by Fono) as RN
From sucursales
), CTE_Users as (
Select
id as id_num
from sucursales
group by id
)
Select
id_num
,a.Fono as Phone_1
,b.Fono as Phone_2
,c.Fono as Phone_3
,d.Fono as Phone_4
,e.Fono as Phone_5
From CTE_Users as realz
Left Join [CTE_Main] as a on a.id = realz.id_num and a.RN = 1
Left Join [CTE_Main] as b on b.id = realz.id_num and b.RN = 2
Left Join [CTE_Main] as c on c.id = realz.id_num and c.RN = 3
Left Join [CTE_Main] as d on d.id = realz.id_num and d.RN = 4
Left Join [CTE_Main] as e on e.id = realz.id_num and e.RN = 5
I know its kind of lengthy but it will display the results in the way that you want them.. My example only uses 5 rows but it should be pretty self explanatory.
Sql Fiddle: http://sqlfiddle.com/#!3/496f6/1

Resources