I have a SQL Server Docs Table with two fields, Idx1 and Idx2:
Idx1 Idx2
0 23
1 34
2 12
4 1
5 21
7 45
8 50
9 3
10 9
... ...
Note that numbers in Idx1 column are unique, they are never repeated.
And now I am trying to re-number Idx1 column starting from 0, that is, 0,1,2,3,4,... and so on.
The expected result should be:
Idx1 Idx2
0 23
1 34
2 12
3 1
4 21
5 45
6 50
7 3
8 9
... ...
I have tried below and it works:
DECLARE #myVar int
SET #myVar = 0
UPDATE
Docs
SET
#myvar = Idx1 = #myVar + 1
but i am worried about in which order SQL Server are numbering them. I would like to explicitly order them first by Idx1 column and then re-number them taking into account this order.
NOTE: I am using SQL Server 2008
There's no need to play around with variables. You could make a subquery and apply sorting inside it to be certain it follows explicit order. There's alternative, modern, approach, which will also work in other database engines supporting window functions.
Use ROW_NUMBER window function available from SQL Server 2008 to create a column with temporary (for the query run) sequence based on order by argument. Then substract 1 from it to make it start from 0.
UPDATE docs
SET idx1 = t.rn
FROM (
SELECT idx1, row_number() over (order by idx1) - 1 as rn
FROM docs
) t
WHERE docs.idx1 = t.idx1
Why not simply use row_number() function :
with t as (
select *, (row_number() over (order by idx1))-1 seq
from docs
)
update t
set t.idx1 = t.seq
from t inner join
docs t1
on t1.idx1 = t.idx1;
Related
I have a set of data that I want to classify into groups based on a prior record id existing on the newer rows. The initial record of the group has a prior sequence id = 0.
The data is as follows:
customer id
sequence id
prior_sequence id
1
1
0
1
2
1
1
3
2
2
4
0
2
5
4
2
6
0
2
7
6
Ideally, I would like to create the following grouping column and yield the following results:
customer id
sequence id
prior sequence id
grouping
1
1
0
1
1
2
1
1
1
3
2
1
2
4
0
2
2
5
4
2
2
6
0
3
2
7
6
3
I've attempted to utilize island gap logic utilizing the ROW_NUMBER() function. However, I have been unsuccessful in doing so. I suspect the need here is more along the lines of a recursive CTE, which I am attempting at the moment.
I agree that a recursive CTE will do the job. Something like:
WITH reccte AS
(
/*query that determines starting point for recursion
*
* In this case we want all records with no prior_sequence_id
*/
SELECT
customer_id,
sequence_id,
prior_sequence_id,
/*establish grouping*/
ROW_NUMBER() OVER (ORDER BY sequence_id) as grouping
FROM yourtable
WHERE prior_sequence_id = 0
UNION
/*join the recursive CTe back to the table and iterate*/
SELECT
yourtable.customer_id,
yourtable.sequence_id,
yourtable.prior_sequence_id,
reccte.grouping
FROM reccte
INNER JOIN yourtable ON reccte.sequence_id = yourtable.prior_sequence_id
)
SELECT * FROM reccte;
It looks like you could use a simple correlated query, at least given your sample data:
select *, (
select Sum(Iif(prior_sequence_id = 0, 1, 0))
from t t2
where t2.sequence_id <= t.sequence_id
) Grouping
from t;
See Example Fiddle
I have a table my_table of the form
rowNumber number ...
1 23
2 14
3 15
4 25
5 19
6 21
7 19
8 37
9 31
...
1000 28
and I want to find the maximum length of an increasing consecutive sequence of the column number. For this example, it will be 3:
14, 15, 25
My idea is to calculate such length for each number:
rowNumber number ... length
1 23 1
2 14 1
3 15 2
4 25 3
5 19 1
6 21 2
7 19 1
8 37 2
9 31 1
...
and then take the maximum. To calculate length, I wrote the following query that is using recursion:
with enhanced_table as (select *
,1 length
from my_table
where rowNumber = 1
union all
(select b.*
,case when b.number > a.number
then a.length + 1
end new_column
from enhanced_table a, my_table b
where b.rowNumber = a.rowNumber + 1
)
select max(length)
from enhanced_table
So, I'm trying to start from rowNumber = 1 and add all other rows consecutively by recursion. I'm getting the maximum recursion 100 has been exhausted before statement completion error.
My question is: should I find a way to increase maximum iterations allowed on the server (given that the query is simple, I think there won't be a problem to run 1000 iterations), or find another approach?
Also, isn't 100 iterations too low of a threshold?
Thank you!
There has to be some default threshold, and that is what Microsoft chose. It's to prevent infinite loops. Besides, looping doesn't perform well in SQL Server and goes against its set-based structure.
You can specify the max recursion you want to set for the individual query. This overrides the default.
select max(length)
from enhanced_table
option (maxrecursion 1000)
Note, option (maxrecursion 0) is the same as unlimited... and can cause an infinte loop
REFERENCE
An incorrectly composed recursive CTE may cause an infinite loop. For
example, if the recursive member query definition returns the same
values for both the parent and child columns, an infinite loop is
created. To prevent an infinite loop, you can limit the number of
recursion levels allowed for a particular statement by using the
MAXRECURSION hint and a value between 0 and 32,767 in the OPTION
clause of the INSERT, UPDATE, DELETE, or SELECT statement. This lets
you control the execution of the statement until you resolve the code
problem that is creating the loop. The server-wide default is 100.
When 0 is specified, no limit is applied. Only one MAXRECURSION value
can be specified per statement
If you wish to declare the maxrecursion parameter in the beginning of the query.
You could try building query something like:
DECLARE #Query NVARCHAR(MAX)
SET #Query = N'
;WITH foo AS (
...
)
SELECT * FROM foo
OPTION (MAXRECURSION ' + CAST(#maxrec AS NVARCHAR) + ');'
and the Execute it using Exec
You could go refer to this answer here:Maxrecursion parameter
I have a table with 106 columns. One of those columns is a "Type" column with 16 types.
I want 16 rows, where the Type is distinct. So, row 1 has a type of "Construction", row 2 has a type of "Elevator PVT", etc.
Using Navicat.
From what I've found (and understood) so far, I can't use Distinct (because that looks across all rows), I can't use Group By (because that's for aggregating data, which I'm not looking to do), so I'm stuck.
Please be gentle- I'm really really new at this.
Below is a part of the table (how can I share this normally?)- it's really big so I didn't share the whole thing. Below is a partial result I'm looking for, where the Violation_Type is unique and the rest of the columns display.
Got it.. Sheesh... (took me forever, but got it...)
D_ID B_ID V_ID V_Type S_ID c_f d_y l_u p_s du_p
------ ------ ------- -------------- ------ ----- ------ ------ ----- ------
184 117 V 032 Elevator PVT 2 8 0 0
4 140 V 100 Construction 1 8 0 0
10 116 V 122 Electric 1 8 2005 0 0
11 117 V 033 Boiler Local 1 0 2005 0 0
You can use ROW_NUMBER for this:
SELECT *
FROM(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY V_Type ORDER BY (SELECT NULL))
FROM tbl
)t
WHERE rn = 1
Modify the ORDER BY depending on what row you want to prioritize.
From the documentation:
Returns the sequential number of a row within a partition of a result
set, starting at 1 for the first row in each partition.
This means that for every row within a partition (specified by the PARTITION BY clause), sql-server assigns a number from 1 depending on the order specified in the ORDER BY clause.
ROW_NUMBER requires an ORDER BY clause. SELECT NULL tells the sql-server that we do not want to enforce a particular order. We just want the rows numbered by partition.
The WHERE rn = 1 obviously filters only rows that has a ROW_NUMBER of 1. This gives you one row for every V_TYPE available.
This is a bit of a weird question, and I know it would probably be easier to not do it in SQL, but it will make my life a lot easier.
Basically I have a single column result-set, and I need to turn that into 3 columns, not based on any criteria.
eg.
1
2
3
4
5
6
7
into:
1 2 3
4 5 6
7
It will always be a fixed 3 column result I need in this case.
Currently I am using a cursor and inserting into a table variable, which seems a bit terrible. There must be a better way.
Thanks
Try this:
declare #t table(n int)
insert #t(n) values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
select [0],[1],[2]
from
(
select n
, (ROW_NUMBER() over (order by n) - 1) % 3 c
, (ROW_NUMBER() over (order by n) - 1) / 3 r
from #t
) x
pivot (max(n) for c in ([0], [1], [2])) p
It's possible, but man is this an ugly requirement. This really belongs in the presentation tier, not in the sql.
WITH original As
(
SELEZCT MyColumn, row_number() over (order by MyColumn) as ordinal
FROM RestOfOriginalQueryHere
),
Grouped As
(
SELECT MyColumn, ordinal / 3 As row, ordinal % 3 As col
FROM original
)
SELECT o1.MyColumn, o2.MyColumn, o3.MyColumn
FROM grouped g1
LEFT JOIN grouped g2 on g2.row = g1.row and g2.col = 1
LEFT JOIN grouped g3 on g2.row = g1.row and g3.col = 2
WHERE g1.col = 0
I have this table ScoreDetails, 2 columns (there are more, but only 2 needed or this query). One is ScoreDate, Score.
The structure is like
2012:03:27: 5:06:37:134 27
2012:03:27: 5:06:37:276 37
2012:03:28: 4:12:97:019 19
2012:03:29: 7:06:37:134 7
2012:03:29: 8:06:37:134 0
2012:04:03: 12:06:37:739 16
2012:04:04: 23:21:15:834 33
2012:04:04: 15:08:24:697 12
2012:04:06: 5:06:37:134 0
2012:04:09: 5:06:37:134 2
2012:04:13: 5:06:37:134 92
What I want is to write a select query, without using temp table or cursor. Such that, I have a column that starts from 1 and keeps on increasing as 2,3 and so on, upto when the score is non-zero. But as soon as a zero is encountered in score column, it resets to 1 and then start again. Like this...
2012:03:27: 5:06:37:134 27 1
2012:03:27: 5:06:37:276 37 2
2012:03:28: 4:12:97:019 19 3
2012:03:29: 7:06:37:134 7 4
2012:03:29: 8:06:37:134 0 0
2012:04:03: 12:06:37:739 16 1
2012:04:04: 23:21:15:834 33 2
2012:04:04: 15:08:24:697 12 3
2012:04:06: 5:06:37:134 0 0
2012:04:09: 5:06:37:134 2 1
2012:04:13: 5:06:37:134 92 2
I am using SQL Server 2008 R2.
You can use common table expressions for that. I defined 2 anchor queries: one for records with 0 score and the other for the first record. Then you build up the result based on previous results until you find 0 score.
with cte
as
(
select ScoreDate, Score, ScoreRank, 0 as Value
from (select ScoreDate, Score, dense_rank() over (order by ScoreDate) ScoreRank
from ScoreDetails) X
where Score = 0
union all
select ScoreDate, Score, ScoreRank, 1 as Value
from (select ScoreDate, Score, dense_rank() over (order by ScoreDate) ScoreRank
from ScoreDetails) X
where Score <> 0 and ScoreRank = 1
union all
select X.ScoreDate, X.Score, X.ScoreRank, cte.Value + 1 as Value
from (select ScoreDate, Score, dense_rank() over (order by ScoreDate) ScoreRank
from ScoreDetails) X
inner join cte
on X.ScoreRank = cte.ScoreRank + 1
and X.Score <> 0
)
select ScoreDate, Score, Value, ScoreRank
from cte
order by ScoreDate
SQL Fiddle Demo
I won't spoil the fun of finding the solution yourself, but I will give you some hints on how to split the problem into smaller pieces:
Find all the records where the score is reset. Let's call this subquery the resetRecords.
Join the records of the original table to the resetRecords, such that every record has "its" reset record (i.e., the reset record that provides the base for its count).
Use ROW_NUMBER() OVER (PARTITION BY ... ) to assign the numbers.
Try to do this one step at a time. Beware: It won't be a simple query, so a solution with temp tables or cursors might be easier to understand and maintain.
Try something like this:
with x as (
select *, sum(case when Score=0 then 1 else 0 end) over(order by ScoreDate) as grp
from ScoreDetails
)
select ScoreDate, Score, row_number() over (partition by grp order by ScoreDate)
from x
order by ScoreDate
(as soon as a zero is encountered in score column, it resets to 1 and then start again, you said)