Count occurrences of field values as they are displayed in order - sql

thanks in advance for the help and sorry for how the "table" looks. Here's my question...
Let's say I have a subquery with this table (imagine the bold as column headers) as its output -
id 1 1 2 3 3 3 3 4 5 6 6 6
action o c o c c o c o o c c c
I would like my new query to output -
id 1 1 2 3 3 3 3 4 5 6 6 6
action o c o c c o c o o c c c
ct 1 2 1 1 2 3 4 1 1 1 2 3
#c 0 1 0 1 2 2 3 0 0 1 2 3
#o 1 1 1 0 0 1 1 1 1 0 0 0
where ct stands for count. Basically, I want to count (for each id) the occurrences of consecutive id and action as they happen. Let me know if this makes sense, and if not, how I can clarify my question.
Note: I realize the lag/lead functions may be helpful in this situation, along with the row_number() function. Looking for as many creative solutions as possible!

You are looking for the row_number() analytic function:
select id, action, row_number() over (partition by id order by id) as ct
from table t;
For #c and #o, you want cumulative sum:
select id, action, row_number() over (partition by id order by id) as ct,
sum(case when action = 'c' then 1 else 0 end) over
(partition by id order by <some column here>) as "#c",
sum(case when action = 'c' then 1 else 0 end) over
(partition by id order by <some column here>) as "#o"
from table t;
The one caveat is that you need a way to specify the order of the rows -- an id or date time stamp or something. SQL result sets and tables are inherently unordered, so there is no idea that one row comes before or after another.

SQL> select id, action,
2 row_number() over(partition by id order by rowid) ct,
3 sum(decode(action,'c',1,0)) over(partition by id order by rowid) c#,
4 sum(decode(action,'o',1,0)) over(partition by id order by rowid) o#
5 from t1
6 /
ID A CT C# O#
---------- - ---------- ---------- ----------
1 o 1 0 1
1 c 2 1 1
2 o 1 0 1
3 c 1 1 0
3 c 2 2 0
3 o 3 2 1
3 c 4 3 1
4 o 1 0 1
5 o 1 0 1
6 c 1 1 0
6 c 2 2 0
6 c 3 3 0
P.S. Sorry Gordon, didn't see your post.

Related

SQL Query to get multiple resultant on single column

I have a table that looks something like this:
id name status
2 a 1
2 a 2
2 a 3
2 a 2
2 a 1
3 b 2
3 b 1
3 b 2
3 b 1
and the resultant i want is:
id name total count count(status3) count(status2) count(status1)
2 a 5 1 2 2
3 b 4 0 2 2
please help me get this result somehow, i can just get id, name or one of them at a time, don't know how to put a clause to get this table at once.
Here's a simple solution using group by and case when.
select id
,count(*) as 'total count'
,count(case status when 3 then 1 end) as 'count(status1)'
,count(case status when 2 then 1 end) as 'count(status3)'
,count(case status when 1 then 1 end) as 'count(status2)'
from t
group by id
id
total count
count(status3)
count(status2)
count(status1)
2
5
1
2
2
3
4
0
2
2
Fiddle
Here's a way to solve it using pivot.
select *
from (select status,id, count(*) over (partition by id) as "total count" from t) tmp
pivot (count(status) for status in ([1],[2],[3])) pvt
d
total count
1
2
3
3
4
2
2
0
2
5
2
2
1
Fiddle

SQL update order in relation ship based on ID

I wonder how to update order in this table for many-to-many relationship using SQL based on PostsId.
So my table now looks like:
I'm using SQL Server
BlogsId
PostsId
Order
1
1
1
0
2
2
1
0
3
3
2
0
3
4
2
0
3
5
3
0
3
6
3
0
but I want to update Order using SQL to this:
BlogsId
PostsId
Order
1
1
1
1
2
2
1
2
3
3
2
1
3
4
2
2
3
5
3
1
3
6
3
2
So for example: Blog with Id 3 is the first blog in Post with Id 2, Blog with Id 4 is the second Blog in Post with Id 2 and etc...
I've tried this:
DECLARE #myVar int
SET #myVar = 0
UPDATE [dbo].[BlogPost]
SET #myVar = [Order] = #myVar + 1
but then I got this:
BlogsId
PostsId
Order
1
1
1
1
2
2
1
2
3
3
2
3
3
4
2
4
3
5
3
5
3
6
3
6
So, I think I should do something in WHERE part (with Distinct maybe) but I don't know exactly what. I could write something in C# to do what I want but I don't know how to write something like this in SQL.
Physically maintaining an order or sequence of rows is rarely a good idea and can lead to data inconsistencies and other unforseen issues.
You would be better off creating a view that provides the additional Order column which you can do using row_number()
Create view BlogPosts as
select *,
Row_Number() over(partition by PostsId order by BlogsId) as [Order]
from blogpost;
If you really want to update an actual column in the table you could use a CTE
with b as (
select *,
Row_Number() over(partition by PostsId order by BlogsId) as seq
from blogpost
)
update b
set [Order] = seq;
You can update from a calculated row_number.
update t
set [Order] = rn
from (
select BlogsId, PostsId, [Order]
, rn = row_number() over (partition by PostsId order by BlogsId asc)
from BlogPost
) t
where ([Order] is null or [Order]!=rn);
select *
from BlogPost
order by BlogsId, PostsId
BlogsId
PostsId
Order
1
1
1
2
1
2
3
2
1
4
2
2
5
3
1
6
3
2
Demo on db<>fiddle here

SQL Order By Custom Sequence

I have a data in this order
Id Value
-- ----
1 a
1 b
1 c
2 a
2 c
3 b
4 c
4 b
4 a
I want to sort data in this order
Id Value
-- ----
1 a
2 a
3 b
4 c
1 b
2 c
4 b
1 c
4 a
You seem to want to intersperse the numbers. For this purpose, you can use row_number():
order by row_number() over (partition by id order by value),
id

Row_Number based on two columns

I am looking to try to create the row number below, I have tried so many options but none seem to work. I would appreciate help.
I want the row order to look at sub_id and for each one do a sequential number, 1, 2, 3... I then want it to look at disc, and if that changes, go back to 1 again. I have tried concatenating them and using a key, but I can't seem to get it to work.
ID sub_id disc ROW_NUMBER
1 1 OT 1
1 1 OT 2
1 1 OT 3
1 2 DT 1
1 2 DT 2
1 3 SL 1
1 3 SL 2
1 4 PH 1
1 4 PH 2
1 4 OT 1
1 5 OT 1
1 5 PH 2
1 5 DT 1
1 6 DT 1
1 6 DT 2
1 6 DT 3
1 7 SL 1
1 7 SL 2
1 7 DT 1
1 8 SL 1
1 8 SL 2
1 8 SL 3
1 9 DT 1
1 9 DT 2
1 9 DT 3
1 10 PH 1
1 10 DT 1
1 10 DT 2
1 11 OT 1
1 11 OT 2
1 11 OT 3
1 12 OT 1
1 12 OT 2
1 12 OT 3
Update
row_number() OVER (PARTITION BY sub_id ORDER BY disc) does not work. The ordering goes wrong when sub_id changes to 2. at this point it needs to revert back to 1, but it is 2.
You seem to want:
row_number() over (partition by id, sub_id, disc order by id)
Note: You do not have a column that specifies the ordering of the columns -- or even uniquely identifies each row. Hence, this just uses order by id. You might want to include some other column there.
SELECT ID , sub_ID , disc , ROW_NUMBER() OVER (PARTITION BY sub_ID ORDER BY disc) from your table

Best way to by column and aggregation on another column

I want to create a rank column using existing rank and binary columns. Suppose for example a table with ID, RISK, CONTACT, DATE. The existing rank is RISK, say 1,2,3,NULL, with 3 being the highest. The binary-valued is CONTACT with 0,1 or FAILURE/SUCESS. I want to create a new RANK that will order by RISK once a certain number of successful contacts has been exceeded.
For example, suppose the constraint is a minimum of 2 successful contacts. Then the rank should be created as follows in the two instances below:
Instance 1. Three ID, all have a min of two successful contacts. In that case the rank mirrors the risk:
ID risk contact date rank
1 3 S 1 3
1 3 S 2 3
1 3 F 3 3
1 3 F 4 3
2 2 S 1 2
2 2 S 2 2
2 2 F 3 2
2 2 F 4 2
3 1 S 1 1
3 1 S 2 1
3 1 S 3 1
Instance 2. Suppose ID=1 has only one successful contact. In that case it is relegated to the lowest rank, rank=1, while ID=2 gets the highest value, rank=3, and ID=3 maps to rank=2 because it satisfies the constraint but has a lower risk value than ID=2:
ID risk contact date rank
1 3 S 1 1
1 3 F 2 1
1 3 F 3 1
1 3 F 4 1
2 2 S 1 3
2 2 S 2 3
2 2 F 3 3
2 2 F 4 3
3 1 S 1 2
3 1 S 2 2
3 1 S 3 2
This is SQL, specifically Hive. Thanks in advance.
Edit - I think Gordon Linoff's code does it correctly. In the end, I used three interim tables. The code looks like that:
First,
--numerize risk, contact
select A.* ,
case when A.risk = 'H' then 3
when A.risk = 'M' then 2
when A.risk = 'L' then 1
when A.risk is NULL then NULL
when A.risk = 'NULL' then NULL
else -999 end as RISK_RANK,
case when A.contact = 'Successful' then 1
else NULL end as success
Second,
-- sum_successes_by_risk
select A.* ,
B.sum_successes_by_risk
from T as A
inner join
(select A.person, A.program, A.risk, sum(a.success) as sum_successes_by_risk
from T as A
group by A.person, A.program, A.risk
) as B
on A.program = B.program
and A.person = B.person
and A.risk = B.risk
Third,
--Create table that contains only max risk category
select A.* ,
B.max_risk_rank
from T as A
inner join
(select A.person, max(A.risk_rank) as max_risk_rank
from T as A
group by A.person
) as B
on A.person = B.person
and A.risk_rank = B.max_risk_rank
This is hard to follow, but I think you just want window functions:
select t.*,
(case when sum(case when contact = 'S' then 1 else 0 end) over (partition by id) >= 2
then risk
else 1
end) as new_risk
from t;