SQL count changes in column - sql

I need to count the changes in assigned group on a ticket. The problem is my log also count changes in assignee that are in the same group.
Here is some sample data
ticket_id | assigned_group | assignee | date
----------------------------------------------------
1001 | group A | john | 1-1-15
1001 | group A | michael | 1-2-15
1001 | group A | jacob | 1-3-15
1001 | group B | eddie | 1-4-15
1002 | group A | john | 1-1-15
1002 | group B | eddie | 1-2-15
1002 | group A | john | 1-3-15
1002 | group B | eddie | 1-4-15
1002 | group A | john | 1-5-15
I need this to return
ticket_id | count
--------------------
10001 | 2
10002 | 4
My query is like this
select ticket_id, assigned_group, count(*) from mytable group by ticket_id, assigned_group
But that gives me
ticket_id | count
--------------------
10001 | 4
10002 | 5
edit:
Also if I use
select ticket_id, count(Distinct assigned_group) as [Count] from mytable group by ticket_id
I only get
ticket_id | count
--------------------
10001 | 2
10002 | 2
Any advice?

Use Distinct Count to get the result
select ticket_id, count(Distinct assigned_group) as [Count]
from mytable
group by ticket_id

try this..
with temp as
(
select ticket_id, assigned_group, count(*) as count,date from mytable group by ticket_id, assigned_group,date
)
select ticket_id, count from temp

You can use Row_number() function to look into the next record's value.
with tbl as (select *, row_number() over(partition by ticket_id order by 1) from table)
select a.ticket_id, a.assigned_group, a.assignee_name, a.date,
count(case when a.assigned_group <> b.assigned_group then 1 else 0 end) as No_of_change
from tbl as a
left join tbl as b
on a.rn = b.rn + 1

If you are using SQL Server 2012, then you can use the LAG function to determine the previous assigned group easily. Then, if the previous assigned group is different from the current assigned group, you can increment the count, as below:
WITH previous_groups AS
(
SELECT
ticket_id,
assign_date,
assigned_group,
LAG(assigned_group, 1, NULL) OVER (PARTITION BY ticket_id ORDER BY assign_date) AS prev_assign_group
FROM mytable
)
SELECT
ticket_id,
SUM(CASE
WHEN assigned_group <> prev_assign_group THEN 1
ELSE 0
END) AS count
FROM previous_groups
WHERE prev_assign_group IS NOT NULL
GROUP BY ticket_id
ORDER BY ticket_id;
If you are using SQL Server 2008 or earlier versions, then you need an extra step to determine the previous assigned group, as below:
WITH previous_assign_dates AS
(
SELECT
mt1.ticket_id,
mt1.assign_date,
MAX(mt2.assign_date) AS prev_assign_date
FROM mytable mt1
LEFT JOIN mytable mt2
ON mt1.ticket_id = mt2.ticket_id
AND mt2.assign_date < mt1.assign_date
GROUP BY
mt1.ticket_id,
mt1.assign_date
),
previous_groups AS
(
SELECT
mt1.*,
mt2.assigned_group AS prev_assign_group
FROM mytable mt1
INNER JOIN previous_assign_dates pad
ON mt1.ticket_id = pad.ticket_id
AND mt1.assign_date = pad.assign_date
LEFT JOIN mytable mt2
ON pad.ticket_id = mt2.ticket_id
AND pad.prev_assign_date = mt2.assign_date
)
SELECT
ticket_id,
SUM(CASE
WHEN assigned_group <> prev_assign_group THEN 1
ELSE 0
END) AS count
FROM previous_groups
WHERE prev_assign_group IS NOT NULL
GROUP BY ticket_id
ORDER BY ticket_id;
SQL Fiddle demo
References:
The LAG function on MSDN

Adding an ordinal number within the ticket, then a self join where the group is different and consecutive ordinals, should work:
SELECT t1.ticket_id, COUNT(*) FROM
(SELECT *, ROW_NUMBER() OVER(PARTITION BY ticket_id ORDER BY date) ordinal
FROM mytable) t1
JOIN
(SELECT *, ROW_NUMBER() OVER(PARTITION BY ticket_id ORDER BY date) ordinal FROM nytable) t2
ON t1.ticket_id=t2.ticket_id AND t1.assigned_group<>t2.assigned_group AND t1.ordinal+1=t2.ordinal
GROUP BY t1.ticket_id

Related

How to get distinct row based on criteria

I have table with data like this
ID | Desc | Status
------+------+------------
1 | abc | Completed
1 | abc | Completed
1 | def | Planned
1 | def | Planned
1 | ghi | Rescheduled
1 | ghi | Rescheduled
2 | abc | Completed
2 | def | Planned
2 | ghi | Planned
I need to get one row for each ID based on Status. One row for Planned and for other, if an ID has both status Completed & Rescheduled, then Rescheduled row must be selected or else Completed
e.g.
ID 1 has Planned, Completed & Rescheduled status. The output should be as below
one row Planned for ID=1 & one row "Rescheduled" for ID=1
ID | Desc | Status
------+------+------------
1 | ghi | Rescheduled
1 | def | Planned
2 | abc | Completed
2 | def | Planned
Your can do it with NOT EXISTS and then return (as your expected output) the minimum value of desc for each combination of id and status:
select t.id, min(t.[desc]) [desc], t.status
from tablename t
where status in ('Planned', 'Rescheduled')
or not exists (
select 1 from tablename
where id = t.id and status = 'Rescheduled'
)
group by t.id, t.status
order by t.id, t.status
See the demo.
Results:
> id | desc | status
> -: | :--- | :----------
> 1 | def | Planned
> 1 | ghi | Rescheduled
> 2 | abc | Completed
> 2 | def | Planned
you can try this
select id, desc, status, 1 count from(
select id, desc, status, ROW_NUMBER() over (partition by id) rownum from(
select distinct id, desc, status, 1 count
from tablename where status = 'Planned') x ) y
where rownum = 1
union all
select id, desc, status, 1 from(
select id, desc, status, ROW_NUMBER() over (partition by id order by status desc) rownum from(
select distinct id, desc, status from tablename where status in ('Rescheduled','Completed')) x ) y
where rownum = 1
order by 1,2,3
You can use CTE to remove duplicate records, then apply CASE WHEN THEN to get desired results like :
WITH CTE_Result AS
(
select distinct Id,description, status
from tdata
)
select Id,
CASE
WHEN description='Planned' THEN 'Planned'
WHEN description='Planned'OR description='Rescheduled' AND (select Count(t.description) from tdata t where t.id=id)>1 THEN 'Rescheduled'
ELSE 'Completed'
END as description,
status
from CTE_Result;
Sample fiddle is http://www.sqlfiddle.com/#!18/cb394/49
You can look at the minimum status per ID, which is either 'Completed' or not:
with data as
(
select
id, status, min([Desc]) as description, min(status) over (partition by id) as min_status
from mytable
group by id, status
)
select id, status, description
from data
where status in ('Planned', 'Completed') or min_status <> 'Completed'
order by id, status;
I figured out using logic from #Krishna Muppalla and #Thorsten Kettner. Thanks you all for your suggestions.
select * from
(
select
distinct ID
,Desc
,Status
,max(Status) over (partition by ID, desc) as New_Status
from tdata
where Status in ('Completed','Rescheduled')
) A
where A.Status = A.New_Status
UNION
select
distinct ID
,Desc
,Status
,max(Status) over (partition by ID, desc) as New_Status
from tdata
where Status in ('Planned')

I want to get minimum of starttime of each id. But I'm not able to achieve it using min function. How to solve it?

Table:
id | starttime | grade
-------------------------
1 | 4PM | A
1 | 5PM | C
2 | 2PM | A
2 | 3PM | B
In output I should get all the ids that have minimum starttime with all the columns.
For the above table output should be like this:
id | starttime | grade
-------------------------
1 | 4PM | A
2 | 2PM | A
Using top with ties and row_number() to get the minimum starttime for each id.
select top (1) with ties *
from t
order by row_number() over (partition by id order by starttime);
rextester demo: http://rextester.com/RJVT1405
returns:
+----+-----------+-------+
| id | starttime | grade |
+----+-----------+-------+
| 1 | 4PM | A |
| 2 | 2PM | A |
+----+-----------+-------+
In SQL Server, use top with ties:
select top (1) with ties t.*
from t
order by starttime;
TOP (1) only returns one row. TOP (1) WITH TIES returns all rows that have the same key value as in the first row specified by the ORDER BY.
You can use group by:
with minTimes (id, startTime) as
(
select id, min(startTime) from myTable
group by id
)
select t1.*
from myTable t1
inner join minTimes t2 on t1.id = t2.id and t1.startTime = t2.startTime
;WITH cte(id,starttime, grade)
AS
(
SELECT 1 , '4PM','A' Union all
SELECT 1 , '5PM','C' Union all
SELECT 2 , '2PM','A' Union all
SELECT 2 , '3PM','B'
)
,Final AS (
SELECT *
,Row_Number() OVER (
PARTITION BY MinStartTime ORDER BY MinStartTime
) Seq
FROM (
SELECT id
,CAST(starttime AS TIME) AS starttime
,MIN(CAST(starttime AS TIME)) OVER (
PARTITION BY ID ORDER BY starttime
) AS MinStartTime
,grade
FROM cte
) Dt
)
SELECT id
,CONVERT(VARCHAR(15), MinStartTime, 100) AS starttime
,grade
FROM final
WHERE Seq = 1
ORDER BY 1

Get latest rows by date from aggregate

Hey i'm kinda stuck with this query. Using SQL-server
i have in the table, UNIQUE(date, medId, userId)
I have this table
date | medId | userId | Quantity
2016-06-10 | 2 | 1 | 28
2016-06-07 | 1 | 1 | 19
2016-06-06 | 1 | 1 | 10
i want to get the row with the max date, per group of medId,userId, in this case
i would get
2016-06-10 | 2 | 1 | 28
2016-06-07 | 1 | 1 | 19
thanks in advance!
i've tried this
SELECT
a.userMedStockDate,
a.userMedStockMedId,
a.userMedStockUserId,
a.userMedStockQuantity
FROM (SELECT
MAX(userMedStockDate) AS userMedStockDate,
userMedStockQuantity,
userMedStockUserId,
userMedStockMedId,
ROW_NUMBER() OVER (partition by userMedStockMedId,userMedStockUserId
ORDER BY MAX(userMedStockDate) desc) AS rnk
FROM UserMedStock
GROUP BY
userMedStockUserId,
userMedStockQuantity,
userMedStockMedId) a
WHERE a.rnk = 1
[SOLVED]
this should work
select * from
(
select
[date] , medId, userId ,Quantity
,row_number() over (partition by medId, userId order by [date] desc) as rowid
from yourtable
) as x
where rowid = 1
Could also try this:
select y.* from
table1 y inner join
(
SELECT [Date] = MAX([Date]), medId, userId
FROM table1
GROUP BY medId, userId
) x on y.[Date] = x.[Date] and y.medId = x.medId and y.userId = x.userId
i changed the fields to my actual table but here
SELECT
a.userMedStockDate, a.userMedStockMedId, a.userMedStockUserId, a.userMedStockQuantity
FROM(
SELECT
MAX(userMedStockDate) AS userMedStockDate,
userMedStockQuantity,
userMedStockUserId,
userMedStockMedId,
ROW_NUMBER()OVER(partition by userMedStockMedId, userMedStockUserId ORDER BY MAX(userMedStockDate) desc) AS rnk
FROM UserMedStock
GROUP BY userMedStockUserId, userMedStockQuantity, userMedStockMedId
) a
WHERE a.rnk = 1

Oracle SQL: Transform rows to multiple columns

I'm using Oracle 11G and need a way to turn rows into new groups of columns in a select statement. We're transitioning to a 1:3 relationship for some of our data and need a way to get it into a view. Can you help us transform data that looks like this:
+---------+------------+
| User_Id | Station_Id |
+---------+------------+
| 1 | 203 |
| 1 | 204 |
| 2 | 203 |
| 3 | 487 |
| 3 | 3787 |
| 3 | 738 |
+---------+------------+
into this:
+---------+-------------+-------------+---------------+
| User_Id | Station_One | Station_Two | Station_Three |
+---------+-------------+-------------+---------------+
| 1 | 203 | 204 | Null |
| 2 | 203 | Null | Null |
| 3 | 487 | 3787 | 738 |
+---------+-------------+-------------+---------------+
Let me know what ever other specifics you would like and thank you for any help you can give!
You can use row_number and self joins:
with cte as
(
select userid, stationid,
row_number() over(partition by userid order by stationid) rn
from tbl
)
select distinct c1.userid,
c1.stationid station_one,
c2.stationid station_two,
c3.stationid station_three
from cte c1
left join cte c2 on c1.userid=c2.userid and c2.rn=2
left join cte c3 on c1.userid=c3.userid and c3.rn=3
where c1.rn=1
See the demo
You can also do it with row_number and subqueries:
with cte as
(
select userid, stationid,
row_number() over(partition by userid order by stationid) rn
from tbl
)
select distinct userid,
(select stationid from cte c where c.userid=cte.userid and c.rn=1) station_one,
(select stationid from cte c where c.userid=cte.userid and c.rn=2) station_two,
(select stationid from cte c where c.userid=cte.userid and c.rn=3) station_three
from cte
See the demo
The easiest way to accomplish this in my experience is to use conditional aggregation:
WITH mydata AS (
SELECT 1 AS user_id, 203 AS station_id FROM dual
UNION ALL
SELECT 1 AS user_id, 204 AS station_id FROM dual
UNION ALL
SELECT 2 AS user_id, 203 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 487 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 3787 AS station_id FROM dual
UNION ALL
SELECT 3 AS user_id, 738 AS station_id FROM dual
)
SELECT user_id
, MAX(CASE WHEN rn = 1 THEN station_id END) AS station_one
, MAX(CASE WHEN rn = 2 THEN station_id END) AS station_two
, MAX(CASE WHEN rn = 3 THEN station_id END) AS station_three
FROM (
SELECT user_id, station_id, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY rownum ) AS rn
FROM mydata
) GROUP BY user_id;
Just replace the mydata CTE in the above query with whatever your table's name is:
SELECT user_id
, MAX(CASE WHEN rn = 1 THEN station_id END) AS station_one
, MAX(CASE WHEN rn = 2 THEN station_id END) AS station_two
, MAX(CASE WHEN rn = 3 THEN station_id END) AS station_three
FROM (
SELECT user_id, station_id, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY rownum ) AS rn
FROM mytable
) GROUP BY user_id;

make a select query with group by

This my table with sample data.
id | path | category (1-6) | secter_id | date
----------------------------------------------
1 | ddd | 5 | a | 10-01
2 | ddgg | 6 | a | 10-03
3 | fff | 5 | a | 10-02
I want to filter the latest category 5 and 6 rows for each sector id.
Expected result
id path | category| secter_id | date
--------------------------------------
2 | ddgg | 6 | a | 10-03
3 | fff | 5 | a | 10-02
Is this possible do only sql?
This query should do it for you
SELECT A.ID,
A.PATH,
A.CATEGORY,
A.SECTOR_ID,
A.dDATE
FROM yourTable A
INNER JOIN
(SELECT CATEGORY,
MAX(dDate) AS dDate
FROM yourTable
GROUP BY CATEGORY) B
ON A.CATEGORY = B.CATEGORY
AND A.dDate = B.dDate
Here is a SQLFiddle with the query
You can try with this code, is not elegant but it should work.
Select id,path,category,secter_id,date
FROM myTable a
INNER JOIN (SELECT category, MAX(date) date FROM myTable GROUP BY Category) b ON a.category = b.Category AND a.date = b.Date
WHERE A.Category IN (5,6)
You can try this -
SELECT id,path,category,secter_id, date
FROM
(
SELECT id,path,category,secter_id, date,
DENSE_RANK() OVER (PARTITION BY category ORDER BY DATE DESC) date_rank
FROM sample_table t
WHERE category in (5,6)
)
WHERE date_rank = 1;
try this
select path,category,secter_id,date from
(
select path,category,secter_id,date,dense_rank() over(PARTITION by category order by date desc)as rk
from tbl WHERE category in (5,6)
)data
where rk=1
select * from (
select
id, path , category, secter_id, date ,
row_number() over (partition by category order by date desc) as rnk
from your_table
)
where rnk = 1;
Try this
SELECT [id]
,[path]
,[category]
,[secter_id]
,[date]
FROM [MyTable]
WHERE date IN (SELECT MAX(date)
FROM [MyTable]
WHERE category IN (SELECT DISTINCT category FROM MyTable)
GROUP BY category)