Get the first occurence of the result in each specified group - sql

I have this query in sql server 2012
select sum(user_number),
sum(media_number),
month_name from (
select TOP 100
count(distinct a.answer_group_guid) as 'user_number',
count(distinct a.media_guid) as 'media_number',
datename(mm,answer_datetime) as 'month_name' ,year(answer_datetime) as 'year'
from
tb_answers as a
left outer join
tb_media as m ON m.user_guid = 'userguid' and m.media_guid=a.media_guid
where
m.user_guid = 'userguid'
group by concat(year(answer_datetime),'',month(answer_datetime)),datename(mm,answer_datetime),year(answer_datetime)
order by year(answer_datetime) desc) as aa
group by month_name,year
order by month_name desc,year desc;
it get this result
Out
user_number media_number month_name
5 1 September
2 1 October
1 1 October
1 1 August
But I need only the first occurence of octuber month
as
user_number media_number month_name
5 1 September
2 1 October
1 1 August

You simply need to use a ranking function like ROW_NUMBER(). Use it to number the records partitioning by month_name, and select only the records which are number 1 in each partition, i.e.
Add this to the select list of your query:
ROW_NUMBER() OVER(PARTITION BY month_name ORDER By XXX) as RowNumber
This will number the rows which have the same month_name with consecutive numbers, starting by 1, and in the order specified by XXX.
NOTE: specify the order in XXX to decide which of the month rows is number one and will be returned by the query
And then, do a select from the resulting query, filtering by RowNumber = 1
SELECT Q.user_number, Q.media_number, Q.month_name
FROM(
-- your query + RowNumber) Q
WHERE Q.RowNumber = 1
NOTE: if you need some ordering in your result, you'll have to move the ORDER BY out of the subselect, and write it beside the WHERE Q.RowNumber=1

Related

find users with consecutive logins sql query

i have table contains 2 columns; customer_id, login_date.
For each day, if a customer has logged in, there will be 1 entry in the table for that customer.
Customer_id login_date
--------------------
1 31-Dec-2018
2 31-Dec-2018
3 31-Dec-2018
1 1-Jan-2019
2 1-Jan-2019
3 2-Jan-2019
2 2-Jan-2019
3 3-Jan-2019
3 4-Jan-2019
I need to get the ids of customers who have logged in for at least 3 consecutive days.
Expected output is like below.
Customer_id
------
2
3
So far i have achieved this using below query .
select customer_id from (
select *, case when (lag(logindate,1) over (partition by customer_id order by logindate)) = dateadd(day, -1,logindate) then 1 else 0 end second_day ,
case when (lag(logindate,2) over (partition by customer_id order by logindate)) = dateadd(day, -2,logindate) then 1 else 0 end third_day
from login_history
) a
where a.second_day =1 and a.third_day =1;
But if i have to get customers with 5 consecutive logins i have to keep on adding lag columns.
Is there any better way to get this done?
You can use lag() or lead():
select distinct customer_id
from (select t.*,
lag(login_date, 2) over (partition by customer_id order by login_date) as prev2_login_date
from t
) t
where prev2_login_date = dateadd(day, -2, login_date);
This looks at the login date two rows behind. If it is two days before the current day -- then voila! Three days in a row. This uses the fact that you do not have duplicate dates for a given customer.
Here is a db<>fiddle.

How to get next to last MAX date with join

I'm working on a query in MS SQL to show items that have been in our "service center" more than once within the last 30 days. The data needed is from the service order prior to the most recent service order, based on serial number. So if an item has been received in the last 30 days, check to see if it was received at a previous time within the last 30 days.
ServiceOrders table: CustID, ItemID, DateReceived
ItemMaster table: CustID, ItemID, SerialNumber
I can get DateReceived items by using
ServiceOrders.DateReceived >= DATEADD(month,-1,GETDATE())
I could load service orders from the past month into a temp table, and then query against that to get the prior service order, but that doesn't sound like the best plan. Any ideas on an efficient way to get the previous service orders?
Example data
ServiceOrders table:
CustID ItemID DateReceived
1 2 9/26/2016
1 2 9/05/2016
1 2 1/15/2015
5 6 9/20/2016
7 6 9/02/2016
ItemMaster table:
CustID ItemID SerialNumber
1 2 8675309
5 6 101
7 6 101
So in the above example, SerialNumber 8675309 and 101 have been received more than once in the last 30 days. I need the data from ServiceOrders and ItemMaster for the DateReceived 9/05/2016 and 09/02/2016 records (the second most recent within 30 days). There are other fields in both tables, but they're simplified here. CustID won't necessarily stay the same from date to date, as the item can be transferred. SerialNumber is the key.
Filter the last month orders into Common Table Expression into cte and number them descending. Then select those items with more than 1 occurrences into cte2, join both cte's selecting the second row.
;With cte as(
Select row_number() over(PARTITION by ItemID order by DateReceived desc) as RowNum, *
from ServiceOrders
where DateReceived >= DateAdd(Month, -1, Getdate())
), cte2 as(
Select
ItemID From cte
Group by ItemID
Having count(*)>1
)
select b.*, c.SerialNumber from cte2 as a
left join cte as b on a.ItemID= b.ItemID and b.RowNum=2
left join ItemMaster as c on b.ItemID=c.ItemID and b.CustID=c.CustID
SELECT *, COUNT(ServiceOrders.DateReceived) as DateCount FROM TABLE
Paste to excel
Filter last column to show results = or > than 2
Might be a quick solution for you.
Get the items which have been received more than once in the last 30 days. Then use row_number to number the rows based on the descending order of date_received. Finally, get the rows whose row_number is 2 (the date before the latest date in the last 30 days).
If serialnumber is needed in the output, just join the itemmaster table to the final resultset.
WITH morethanone
AS (SELECT
so.itemid
FROM serviceorders so
JOIN itemmaster i
ON i.itemid = so.itemid
GROUP BY so.itemid
HAVING COUNT(CASE WHEN DATEDIFF(dd, so.datereceived, GETDATE()) <= 30 THEN 1 END)>1
)
SELECT
custid,
itemid,
datereceived
FROM (SELECT
*,
ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY datereceived DESC) rn
FROM serviceorders
WHERE itemid IN (SELECT itemid FROM morethanone)
AND DATEDIFF(dd, datereceived, GETDATE()) <= 30
) x
WHERE rn = 2

Sort Numbers in varchar value in SQL Server

My Goal is to load a monthly-daily tabular presentation of sales data with sum total and other average computation at the bottom,
I have one data result set with one column that is named as 'Day' which corresponds to the days of the month, with automatic datatype of int.
select datepart(day, a.date ) as 'Day'
On my second result set, is the loading of the sum at the bottom, it happens that the word 'Sum' is aligned to the column of Day, and I used Union All TO COMBINE the result set together, expected result set is something to this like
day sales
1 10
2 20
3 30
4 10
5 20
6 30
.
.
.
31 10
Sum 130
What I did is to convert the day value, originally in int to varchar datatype. this is to successfully join columns and it did, the new conflict is the sorting of the number
select * from #SalesDetailed
UNION ALL
select * from #SalesSum
order by location, day
Assuming your union query returns the correct results, just messes up the order, you can use case with isnumeric in the order by clause to manipulate your sort:
SELECT *
FROM
(
SELECT *
FROM #SalesDetailed
UNION ALL
SELECT *
FROM #SalesSum
) u
ORDER BY location,
ISNUMERIC(day) DESC,
CASE WHEN ISNUMERIC(day) = 1 THEN cast(day as int) end
The isnumeric will return 1 when day is a number and 0 when it's not.
Try this
select Day, Sum(Col) as Sales
from #SalesDetailed
Group by Day With Rollup
Edit (Working Sample) :
select
CASE WHEN Day IS NULL THEN 'SUM' ELSE STR(Day) END as Days,
Sum(Sales) from
(
Select 1 as Day , 10 as Sales UNION ALL
Select 2 as Day , 20 as Sales
) A
Group by Day With Rollup
EDIT 2:
select CASE WHEN Day IS NULL THEN 'SUM' ELSE STR(Day) END as Days,
Sum(Sales) as Sales
from #SalesDetailed
Group by Day With Rollup

order unique accounts by date

I have a table:
create table remote (account int ,datecreated datetime,status int)
insert into remote (account , datecreated,status)
values
(123,'2015-08-25',1),
(123,'2015-08-25',1),
(123,'2015-09-26',1),
(1238,'2015-08-25',1),
(123,'2014-08-25',1),
(123,'2014-08-26',1),
(1238,'2014-08-25',1),
(1238,'2014-08-25',1),
(1235,'2014-08-25',1),
(1234,'2014-09-22',1),
(1234,'2014-09-22',1),
(1234,'2014-10-29',1),
(1236,'2014-10-25',1);
From here I would like to get the unique account count for each month/year where status=1
For example using the data above:
the output would be
count | month
-------------
1 |9/2015
2 |8/2015
2 |10/2014
1 |9/2014
3 |8/2014
How can I make this work?
I use sql 2012.
Use Group by month and year of datecreated to skip day part in count. use the same month and year in order by desc . Then Concatenate the month and year to get the result
SELECT [Count],
[Mon/Year]= CONVERT(VARCHAR(2), [Month]) + '/' + CONVERT(VARCHAR(4), [year])
FROM (SELECT [year]=Year(datecreated),
[month]= Month(datecreated),
[Count]= Count(distinct account)
FROM remote
GROUP BY Year(datecreated),
Month(datecreated)) a
ORDER BY [year] DESC,[Month] DESC
Result
Count Mon/Year
----- --------
1 9/2015
3 8/2015
2 10/2014
1 9/2014
5 8/2014
This is a group by query with a filter and some datetime logic:
select year(datecreated) as yr, month(datecreated) as mon, count(*)
from remote
where status = 1
group by year(datecreated), month(datecreated)
order by yr desc, mon desc;
This puts the year and month into separate columns. You can concatenate them together into a single value if you really want to.

Select top Values For each day of Specified month

I am sorry if the question is silly because i'm new to SQL Server. I want to select top 5 records for each day of specified month.
e.g.
top 5 records for day 1 in month september
top 5 records for day 2 in month september
top 5 records for day 3 in month september
.
.
top 5 records for day 31 in month september
and show these all records as a one result.
Let's say you're checking speeding records for the month June 2012, and you wanted the top 5 speeds (by speed desc).
SELECT *
FROM (
SELECT *, RowNum = Row_number() over (partition by Cast(EventTime as Date)
order by Speed desc)
FROM Events
WHERE EventTime >= '20120601'
AND EventTime < '20120701'
) X
WHERE RowNum <= 5
Try this one,
WITH TopFiveRecords
AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dayColumn ORDER BY colName DESC) RN
FROM tableName
)
SELECT *
FROM TopFiveRecords
WHERE RN <= 5
-- AND date condition here ....
dayColumn the column that contains the date of the month
colName the column to be sorted