How to partition by range - tsql

I'm trying to separate this table into 3 partitions and create a column with which partition the row is in. This table keeps historical data about documents by adding new rows and sets IsDeleted = 1 for old rows. You can see that each revision of the document deletes all of the lines of the old version and recreates it with the new line numbers.
I'm not sure where to start as I haven't used the partition clause before and any help is appreciated.
Current Table:
+----+----------------+------------+-----------+-------------------------+
| ID | DocumentNumber | LineNumber | IsDeleted | CreatedDate |
+----+----------------+------------+-----------+-------------------------+
| 1 | D001 | 1 | 1 | 2017-01-20 14:10:13.533 |
| 2 | D001 | 2 | 1 | 2017-01-20 14:10:13.533 |
| 3 | D001 | 3 | 1 | 2017-01-20 14:10:13.533 |
| 4 | D001 | 4 | 1 | 2017-01-20 14:10:13.533 |
| 5 | D001 | 1 | 1 | 2017-01-21 12:11:14.500 |
| 6 | D001 | 2 | 1 | 2017-01-21 12:11:14.500 |
| 7 | D001 | 1 | 0 | 2017-01-21 15:20:20.222 |
| 8 | D001 | 2 | 0 | 2017-01-21 15:21:21.111 |
+----+----------------+------------+-----------+-------------------------+
Expected Result:
+----+----------------+------------+-----------+-------------------------+-----------------+
| ID | DocumentNumber | LineNumber | IsDeleted | CreatedDate | PartitionNumber |
+----+----------------+------------+-----------+-------------------------+-----------------+
| 1 | D001 | 1 | 1 | 2017-01-20 14:10:13.533 | 1 |
| 2 | D001 | 2 | 1 | 2017-01-20 14:10:13.533 | 1 |
| 3 | D001 | 3 | 1 | 2017-01-20 14:10:13.533 | 1 |
| 4 | D001 | 4 | 1 | 2017-01-20 14:10:13.533 | 1 |
| 5 | D001 | 1 | 1 | 2017-01-21 12:11:14.500 | 2 |
| 6 | D001 | 2 | 1 | 2017-01-21 12:11:14.500 | 2 |
| 7 | D001 | 1 | 0 | 2017-01-21 15:20:20.222 | 3 |
| 8 | D001 | 2 | 0 | 2017-01-21 15:21:21.111 | 3 |
+----+----------------+------------+-----------+-------------------------+-----------------+
UPDATE:
In addition to Jason's answer, I added a partition by clause in order to reset the ranking for each document in my table. I hope this helps someone in the future.
SELECT ID,
DocumentNumber,
LineNumber,
IsDeleted,
CreatedDate,
SUM(CASE WHEN LineNumber = 1 THEN 1 ELSE 0 END)
OVER (PARTITION BY DocumentNumber ORDER BY CreatedDate)
AS 'PartitionNumber'
FROM CurrentTable

I got what your looking for by doing this:
SELECT ID,DocumentNumber,LineNumber,IsDeleted,CreatedDate,
SUM(CASE WHEN LineNumber = 1 THEN 1 ELSE 0 END)
OVER (ORDER BY ID,DocumentNumber,LineNumber,IsDeleted,CreatedDate)
AS 'PartitionNumber'
FROM CurrentTable
GROUP BY ID,DocumentNumber,LineNumber,IsDeleted,CreatedDate
I used SUM and CASE to assign a value of 1 to all line number 1's and a 0 to the others. Then I used a window function to calculate a running total.
Results:
+----+----------------+------------+-----------+-------------------------+----------------+
| ID | DocumentNumber | LineNumber | IsDeleted | CreatedDate | PartitionNumber|
+----+--- ------------+------------+-----------+-------------------------+----------------+
| 1 | D001 | 1 | 1 | 2017-01-20 14:10:13.533 | 1 |
| 2 | D001 | 2 | 1 | 2017-01-20 14:10:13.533 | 1 |
| 3 | D001 | 3 | 1 | 2017-01-20 14:10:13.533 | 1 |
| 4 | D001 | 4 | 1 | 2017-01-20 14:10:13.533 | 1 |
| 5 | D001 | 1 | 1 | 2017-01-21 12:11:14.500 | 2 |
| 6 | D001 | 2 | 1 | 2017-01-21 12:11:14.500 | 2 |
| 7 | D001 | 1 | 0 | 2017-01-21 15:20:20.223 | 3 |
| 8 | D001 | 2 | 0 | 2017-01-21 15:21:21.110 | 3 |
+----+--- ------------+----------------------------------- --------------+----------------+

Is the createdDate is same for each partition...as in partition 3 it is different. If it is same then you can use DENSE_Rank()
SELECT *,
DENSE_RANK() OVER(PARTITION BY documentNumber,CreatedDate ORDER BY documentNumber,CreatedDate ) as PartitionNumber
FROM Table

I think I follow you on this. The below gives you what you want but it will go into more partitions than 3 if there are more in the data, I assume that is expected.
if object_id('tempdb.dbo.#test') is not null drop table #test
create table #test
(
id int,
linenumber int,
isdeleted bit,
createddate datetime,
documentnumber varchar(50)
)
insert into #test
select 1 , 1 , 1 , '2017-01-20 14:10:13.533', 'D001'
union all select 2 , 2 , 1 , '2017-01-20 14:10:13.533', 'D001'
union all select 3 , 3 , 1 , '2017-01-20 14:10:13.533', 'D001'
union all select 4 , 4 , 1 , '2017-01-20 14:10:13.533', 'D001'
union all select 5 , 1 , 1 , '2017-01-21 12:11:14.500', 'D001'
union all select 6 , 2 , 1 , '2017-01-21 12:11:14.500', 'D001'
union all select 7 , 1 , 0 , '2017-01-21 15:20:20.222', 'D001'
union all select 8 , 2 , 0 , '2017-01-21 15:21:21.111', 'D001'
select
*,
DENSE_RANK() over (partition by documentNumber order by isdeleted desc, case when isdeleted=0 then getdate() else createddate end) as partitionValues
from #test

Related

PostgreSQL limit by group, only show first 2 store options

I need to select first 2 lines where the store_name is different than one given for a given product
id | store_name | prod_name
----+------------+------
1 | 1 | A
2 | 1 | B
3 | 1 | C
4 | 1 | A
5 | 2 | E
6 | 2 | A
7 | 3 | G
8 | 2 | A
9 | 1 | A
10 | 3 | A
(10 rows)
result should be store_name <> 3 AND prod_name ='A'
id | store_name | prod_name
----+------------+------
1 | 1 | A
4 | 1 | A
6 | 2 | A
8 | 2 | A
Use the row_number() window function to accomplish this.
Query #1
with first_two as (
select *,
row_number() over (partition by store_name
order by id) as rn
from store_product
where store_name <> 3
and prod_name = 'A'
)
select id, store_name, prod_name
from first_two
where rn <= 2;
| id | store_name | prod_name |
| --- | ---------- | --------- |
| 1 | 1 | A |
| 4 | 1 | A |
| 6 | 2 | A |
| 8 | 2 | A |
View on DB Fiddle

count continuously postgresql data

i need help with counting some data
this what i want
| user_id | action_id | count |
-------------------------------------
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 2 | 2 |
| 4 | 3 | 1 |
| 5 | 3 | 2 |
| 6 | 3 | 3 |
| 7 | 4 | 1 |
| 8 | 5 | 1 |
| 9 | 5 | 2 |
| 10 | 6 | 1 |
this is what i have
| user_id | action_id | count |
-------------------------------
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 2 | 1 |
| 4 | 3 | 1 |
| 5 | 3 | 1 |
| 6 | 3 | 1 |
| 7 | 4 | 1 |
| 8 | 5 | 1 |
| 9 | 5 | 1 |
| 10 | 6 | 1 |
i really need it for create some research about second action from users
how do i do it?
thank you
Using ROW_NUMBER should work here:
SELECT
user_id,
action_id,
ROW_NUMBER() OVER (PARTITION BY action_id ORDER BY user_id) count
FROM yourTable
ORDER BY
user_id;
Demo

Populate zero values in column with next value greater than zero

I have the following Postres code:
SELECT
a.assessmentid,
b.groupid
FROM wo_assessment a
LEFT JOIN wo_group_info b ON a.assessmentid = b.assessmentid
WHERE a.workorderid=2
ORDER BY a.assessmentid
Which returns the following results:
|-------------------|------------|
| assessmentid | groupid |
|-------------------|------------|
| 5 | 5 |
|-------------------|------------|
| 6 | 4 |
|-------------------|------------|
| 7 | 0 |
|-------------------|------------|
| 8 | 5 |
|-------------------|------------|
| 9 | 0 |
|-------------------|------------|
| 10 | 0 |
|-------------------|------------|
I would like to populate the 0 values in the groupid field with the next number above in that column, that isn't 0.
So for example I want my table to look like this:
|-------------------|------------|
| assessmentid | groupid |
|-------------------|------------|
| 5 | 5 |
|-------------------|------------|
| 6 | 4 |
|-------------------|------------|
| 7 | 4 |
|-------------------|------------|
| 8 | 5 |
|-------------------|------------|
| 9 | 5 |
|-------------------|------------|
| 10 | 5 |
|-------------------|------------|
Here is what worked for me:
SELECT q.assessmentid,
first_value(b.groupid ) over (partition by value_partition order by q.assessmentid) FROM (
SELECT a.assessmentid,
b.groupid ,
sum(case when b.groupid is null then 0 else 1 end) over (order by a.assessmentid) as value_partition
FROM wo_assessment as a
LEFT JOIN wo_group_info b ON a.assessmentid = b.assessmentid
ORDER BY a.assessmentid ) as q
LEFT JOIN wo_group_info b ON q.assessmentid = b.assessmentid

SQL Select with root parent

I have a table Members(id, name, parent_id), where parent_id is the parent of the member(it is also a member which can have its parent). For example
id | name | parent_id
----------------------
1 | John | NULL
2 | Smith| 1
3 | Andy | 1
4 | Joe | 2
5 | Rick | 2
6 | Craig| 5
7 | Greg | NULL
8 | Bob | 5
9 | Mike | 8
And I'd like to run statement select from members, and I want to have
id | name | parent_id | root_parent_id
--------------------------------------
1 | John | NULL | NULL
2 | Smith| 1 | 1
3 | Andy | 1 | 1
4 | Joe | 2 | 1
5 | Rick | 2 | 1
6 | Craig| 5 | 1
7 | Greg | NULL | NULL
8 | Bob | 7 | 7
9 | Mike | 8 | 7
I want to find the root_parent_id for all members as deeply as possible. Help me please
with recursive recursive_members as (
select *, id root_id, 1 depth
from members
union all
select r.id, r.name, r.parent_id, m.parent_id, r.depth+ 1
from recursive_members r
join members m on r.root_id = m.id
where m.parent_id notnull
)
select distinct on (id) *
from recursive_members
order by id, depth desc;
id | name | parent_id | root_id | depth
----+-------+-----------+---------+-------
1 | John | | 1 | 1
2 | Smith | 1 | 1 | 2
3 | Andy | 1 | 1 | 2
4 | Joe | 2 | 1 | 3
5 | Rick | 2 | 1 | 3
6 | Craig | 5 | 1 | 4
7 | Greg | | 7 | 1
8 | Bob | 5 | 1 | 4
9 | Mike | 8 | 1 | 5
(9 rows)
Read about recursive WITH queries.

group by breaks order by

Sorry for the long post I'm new to this & want to make sure that I'm fully understood.
I'm trying to make an order by & group query.
I've started with the order by part:
SELECT "tId", "mId","sId","tr", "tg","tp", "date"
FROM table
WHERE "tId" =1
ORDER BY "date" DESC, "mId","sId";
the ouput:
tId | mId | sId | tr | tg | tp | date
-----+-------+------+-----+----+-------+------------------------
1 | 5 | 2 | -73 | 1 | 122 | 2007-01-01 02:03:01+02
1 | 5 | 1 | -72 | 1 | 122 | 2007-01-01 02:02:01+02
1 | 4 | 1 | -70 | 1 | 120 | 2007-01-01 01:01:01+02
1 | 1 | 1 | -30 | 0 | 0 | 2004-10-19 10:23:54+02
1 | 1 | 2 | -31 | 0 | 0 | 2004-10-19 10:23:54+02
1 | 1 | 3 | -32 | 0 | 0 | 2004-10-19 10:23:54+02
1 | 2 | 1 | -40 | 0 | 0 | 2004-10-19 10:23:54+02
1 | 2 | 2 | -41 | 0 | 0 | 2004-10-19 10:23:54+02
1 | 2 | 3 | -42 | 0 | 0 | 2004-10-19 10:23:54+02
1 | 3 | 1 | -50 | 0 | 0 | 2004-10-19 10:23:54+02
1 | 3 | 3 | -50 | 0 | 0 | 2004-10-19 10:23:54+02
The query I would like to do is to group the output of the prev' result and to get:
mId | agg_r | agg_tg | agg_tp | agg_sid | agg_date
-----+--------------+---------+-----------+----------+------------------------------------------------------------------------------
5 | {-73,-72} | {1,1} | {122,122} | {2,1} | {"2007-01-01 02:03:01+02","2007-01-01 02:02:01+02"}
4 | {-70} | {1} | {120} | {1} | {"2007-01-01 01:01:01+02"}
1 | {-30,-31,-32} | {0,0,0} | {0,0,0} | {1,2,3} | {"2004-10-19 10:23:54+02","2004-10-19 10:23:54+02","2004-10-19 10:23:54+02"}
2 | {-40,-41,-42} | {0,0,0} | {0,0,0} | {1,2,3} | {"2004-10-19 10:23:54+02","2004-10-19 10:23:54+02","2004-10-19 10:23:54+02"}
3 | {-50,-50} | {0,0} | {0,0} | {1,3} | {"2004-10-19 10:23:54+02","2004-10-19 10:23:54+02"}
So I've assumed this would work:
SELECT "mId", array_agg("tr") AS agg_r, array_agg("tg") AS agg_tg, array_agg("tp") AS agg_tp, array_agg("sId") AS agg_sid ,array_agg("date") AS agg_date
FROM (
SELECT "tId", "mId","sId","tr", "tg","tp", "date"
FROM table
WHERE "tId" =1
ORDER BY "date" DESC, "mId","sId"
)AS qRes
GROUP BY qRes."mId";
But I'm getting:
mId | agg_r | agg_tg | agg_tp | agg_sid | agg_date
-----+--------------+---------+-----------+----------+------------------------------------------------------------------------------
1 | {-30,-31,-32} | {0,0,0} | {0,0,0} | {1,2,3} | {"2004-10-19 10:23:54+02","2004-10-19 10:23:54+02","2004-10-19 10:23:54+02"}
4 | {-70} | {1} | {120} | {1} | {"2007-01-01 01:01:01+02"}
2 | {-40,-41,-42} | {0,0,0} | {0,0,0} | {1,2,3} | {"2004-10-19 10:23:54+02","2004-10-19 10:23:54+02","2004-10-19 10:23:54+02"}
3 | {-50,-50} | {0,0} | {0,0} | {1,3} | {"2004-10-19 10:23:54+02","2004-10-19 10:23:54+02"}
5 | {-73,-72} | {1,1} | {122,122} | {2,1} | {"2007-01-01 02:03:01+02","2007-01-01 02:02:01+02"}
What am I missing? why does the grouping changes the order?
Like the comment says, there isn't any order on the outer query.
Notice the last line.
SELECT "mId", array_agg("tr") AS agg_r, array_agg("tg") AS agg_tg, array_agg("tp") AS agg_tp, array_agg("sId") AS agg_sid ,array_agg("date") AS agg_date
FROM (
SELECT "tId", "mId","sId","tr", "tg","tp", "date"
FROM table
WHERE "tId" =1
ORDER BY "date" DESC, "mId","sId"
)AS qRes
GROUP BY qRes."mId"
order by max("date") desc;