count consecutive statuses from each ID - sql

I am trying to find a list of clients that have at least 3 consecutive items that are "processed". The following is what my table looks like:
ClientID ItemID Status
1 1 Pending
1 2 Processed
1 3 Processed
2 4 Processed
2 5 Processed
1 6 Processed
1 7 Pending
2 8 Pending
2 9 Processed
3 10 Pending
3 11 Pending
2 12 Processed
3 13 Pending
2 14 Processed
1 15 Processed
2 16 Processed
Expected results:
1 (since it had 3 consecutive processed records from 2, 3, 6 )
2 (since it had 4 consecutive processed records from 9, 12, 14, 16)
As you can see, I define "consecutive" as the next record with the same ClientID and not as the next record in the table, this is what I am having trouble with. My counter restarts when the next clientid in the table is different.
my attempt:
WITH count
AS
(
SELECT *, COUNT(1) OVER(PARTITION BY clientid, count) NotPending
FROM (
SELECT *, (
SELECT COUNT(ItemId)
FROM ##temp a
WHERE status like '%pend%'
AND ItemId < b.ItemId) AS count
FROM ##temp b
WHERE status not like '%pend%'
) t1
)
SELECT distinct clientid from count where NotPending >= 3

You can use row_number() to place rows with the same consecutive status in the same group:
select *,
row_number() over (partition by ClientId order by ItemId)
- row_number() over (partition by ClientId, ItemStatus order by ItemId) as groupName
from Table1
order by ClientId, ItemId
Then you can count the number of entries per group:
select distinct ClientId, count(*) from (
select *,
row_number() over (partition by ClientId order by ItemId)
- row_number() over (partition by ClientId, ItemStatus order by ItemId) as groupName
from Table1
) t
where ItemStatus = 'Processed'
group by ClientId, groupName
having count(*) >= 3
Demo

In your example there are no clients with more than 5 consecutive items (and in your Select you check for >= 10).
Looking for 3 items, returns 1 & 2 for your example data:
WITH cte AS
(
SELECT ClientID, ItemID, Status,
-- returns 3 when there's only 'Processed'
Sum(CASE WHEN Status = 'Processed' THEN 1 end)
Over (PARTITION BY ClientID
ORDER BY ItemId
-- 3 rows including current row
ROWS 2 Preceding) AS Cnt
FROM ##temp
)
SELECT DISTINCT ClientID
FROM cte
WHERE Cnt = 3

Related

SQL : Return joint most frequent values from a column

I have the following table named customerOrders.
ID user order
1 1 2
2 1 3
3 1 1
4 2 1
5 1 5
6 2 4
7 3 1
8 6 2
9 2 2
10 2 3
I want to return to users with most orders. Currently, I have the following QUERY:
SELECT user, COUNT(user) AS UsersWithMostOrders
FROM customerOrders
GROUP BY user
ORDER BY UsersWithMostOrders DESC;
This returns me all the values grouped by total orders like.
user UsersWithMostOrders
1 4
2 4
3 1
6 1
I only want to return the users with most orders. In my case that would be user 1 and 2 since both of them have 4 orders. If I use TOP 1 or LIMIT, it will only return the first user. If I use TOP 2, it will only work in this scenario, it will return invalid data when top two users have different count of orders.
Required Result
user UsersWithMostOrders
1 4
2 4
You can use TOP 1 WITH TIES:
SELECT TOP 1 WITH TIES
[user], COUNT(*) AS UsersWithMostOrders
FROM customerOrders
GROUP BY [user]
ORDER BY UsersWithMostOrders DESC;
See the demo.
Results:
> user | UsersWithMostOrders
> ---: | ------------------:
> 1 | 4
> 2 | 4
Option 1
Should work with most versions of SQL.
select *
from (
select *,
rank() over(order by numOrders desc) as rrank
from (
select `user`, count(*) as numOrders
from customerOrders
group by `user`
) summed
) ranked
where rrank = 1
Play around with the code here
Option 2
If your version of SQL allows window functions (with), here is a much more readable solution which does the same thing
with summed as (
select `user`, count(*) as numOrders
from customerOrders
group by `user`
),
ranked as (
select *,
rank() over(order by numOrders desc) as rrank
from summed
)
select *
from ranked
where rrank = 1
Play around with the code here
You can use a CTE to attain this Req:
;WITH CTE AS(
SELECT [user], COUNT(user) AS UsersWithMostOrders
FROM #T
GROUP BY [user])
SELECT M.* from CTE M
INNER JOIN ( SELECT
MAX(UsersWithMostOrders) AS MaximumOrders FROM CTE) S ON
M.UsersWithMostOrders=S.MaximumOrders
Below Oracle Query can help:
WITH test_table AS
(
SELECT user, COUNT(order) AS total_order , DENSE_RANK() OVER (ORDER BY
total_order desc) AS rank_orders FROM customerOrders
GROUP BY user
)
select * from test_table where rank_orders = 1

How to select top 2 values for each id

I have a table with values
id sales date
1 5 "2015-01-04"
1 3 "2015-01-03"
1 1 "2015-01-01"
1 1 "2015-01-01"
2 7 "2015-01-05"
2 6 "2015-01-04"
2 4 "2015-01-03"
3 11 "2015-01-08"
3 10 "2015-01-07"
3 9 "2015-01-06"
3 8 "2015-01-05"
I want to select top two values of each id as shown in desired output.
Desired output:
id sales date
1 5 "2015-01-04"
1 3 "2015-01-03"
2 7 "2015-01-05"
2 6 "2015-01-04"
3 11 "2015-01-08"
3 10 "2015-01-07"
My attempt:
can someone help me with this. Thank you in advance!
select transactions.salesperson_id, transactions.id, transactions.date
from transactions
ORDER BY transactions.salesperson_id ASC, transactions.date DESC;
This can be done using window functions:
select id, sales, "date"
from (
select id, sales, "date",
dense_rank() over (partition by id order by "date" desc) as rnk
from transactions
) t
where rnk <= 2;
If there are multiple rows on the same date this might return more than two rows for the same ID. If you don't want that, use row_number() instead of dense_rank()
row_number() will get what you want.
select * from
(select row_number() over (partition by id order by date) as rn, sales, date from transactions) t1
where t1.rn <= 2

How to Generate Row number Partition by two column match in sql

Tbl1
---------------------------------------------------------
Id Date Qty ReOrder
---------------------------------------------------------
1 1-1-18 1 3
2 2-1-18 0 3
3 3-1-18 2 3
4 4-1-18 3< >3
5 5-1-18 2 3
6 6-1-18 0 3
7 7-1-18 1 3
8 8-1-18 0 3
---------------------------------------------------------
I want the result like below
---------------------------------------------------------
Id Date Qty ReOrder
---------------------------------------------------------
1 1-1-18 1 3
5 5-1-18 2 3
---------------------------------------------------------
if ReOrder not same with Qty then date will be same upto after reorder=Qty
You can use cumulative approach with row_number() function :
select top (1) with ties *
from (select *, max(case when qty = reorder then 'v' end) over (order by id desc) grp
from table
) t
order by row_number() over(partition by grp order by id);
Unfortunately this will require SQL Server, But you can also do:
select *
from (select *, row_number() over(partition by grp order by id) seq
from (select *, max(case when qty = reorder then 'v' end) over (order by id desc) grp
from table
) t
) t
where seq = 1;

divide data in sql to groups order by another column

I have this set of data
shopId companyId date
1 1 25/8/2015
2 1 26/8/2015
3 1 22/8/2015
4 2 20/8/2015
5 2 27/8/2015
what i need is to get this result
shopId companyId date dense_rank
1 2 27/8/2015 1
2 2 20/8/2015 1
3 1 26/8/2015 2
4 1 25/8/2015 2
5 1 22/8/2015 2
how to get all groups ranked but order with date
SELECT *
, DENSE_RANK() OVER (ORDER BY companyId DESC, [Date] DESC) AS [DENSE_RANK]
FROM TableName
If you want the groups ordered by date, then you need two steps: first get the maximum date for each group. Then use dense_rank():
select shopid, companyid, date,
dense_rank() over (order by maxd desc) as dense_rank
from (select t.*, max(date) over (partition by companyid) as maxd
from table t
) t
Note: this assumes that your date is really stored as a date and not as a string. You will need additional transformations if the data is (improperly) stored as a string.

How to get the last inserted value for every id in SQL Server 2008?

I have to count the unique status from multiple values. Here is my table example
Id Status OrderId
-------------------
1 1 43
2 2 43
3 1 44
Desired output
It should give the count(status) for Status '1' is 1 and Status '2' is 1. But when using count its giving 2 for status '1'.
You have to do
count(DISTINCT status)
instead of
count(status)
to get
unique status from multiple values.
EDIT:
If you want to get (not count) the Status value of the last inserted record for every OrderId, then you can do:
SELECT Status
FROM (
SELECT Id, Status, OrderId,
ROW_NUMBER() OVER (PARTITION BY OrderId
ORDER BY Id DESC) AS rn
FROM mytable ) t
WHERE t.rn = 1
If you want to get the last status for each order:
with cte as(select *, row_number()
over(partition by OrderID order by Id desc) from TableName)
select * from cte where rn = 1
Or:
select * from (select *, row_number()
over(partition by OrderID order by Id desc) from TableName) t
where rn = 1