Reputation: 155
Hi everyone thanks for taking some time to look into my question,
Background
I'm using the ROW_NUMER() function along with a PARTITION BY..ORDER BY statement to set a varchar value with an incrementing int value at the end of it for each of a value in one of my tables.
SELECT component_module_id as component_module_id,
component_property_id,
'cf' + CAST(ROW_NUMBER() OVER (PARTITION BY component_module_id ORDER BY component_module_id) as VARCHAR) AS cf
FROM component_property
Here the 'cf' value goes from cf1-cfX for each component_module_id
My Question
Whenever I attempt to use these cf values elsewhere, like saved in a temp table, other Ordering and Grouping statements change these values. It's like the statements to generate the 'cf' values are saved and not the 'cf' values themselves.
After inserting the query above into a temp table #t -
SELECT * FROM #t ORDER BY cf
I receive 'cf' values that start at cf1 and jump to cf10 and then cf100, with a range of cf values from cf1 to cf900... I should only be receiving values ranging from c1 to cf29.
My question here is - Why are the values in this column treated differently than any other normal value? Why is the ROW_NUMBER() OVER (PARTITION BY....)) calculation being passed to further queries down the line? (if that's actually what's happening). And finally, how I can treat these 'cf' values like normal VARCHAR values and not have them change on me whenever I try to group or order by them.
Thanks for any help!
Update
I took the suggestion from Larnu,
"Seems like you'd be better off just storing the int value, and using a (PERSISTED) computed column to concatenate your prefix and ROW_NUMBER value."
and my 'cf' values are now appearing correctly after being sorted. Thanks everyone marking as solved.
Upvotes: 8
Views: 95360
Reputation: 155
Update
I took the suggestion from Larnu,
"Seems like you'd be better off just storing the int value, and using a (PERSISTED) computed column to concatenate your prefix and ROW_NUMBER value."
and my 'cf' values are now appearing correctly after being sorted. Thanks everyone marking as solved.
Upvotes: -1
Reputation: 1270763
The issue is that you are both partitioning by and ordering by component_module_id
. Within each partition, the ordering keys have the same value.
Why does this matter? Sorting in SQL is NOT stable. That means that ties can be resolved either way. Run the query a second time, and you might get a different ordering (among the ties).
The simple solution is to change the ordering to be stable, and you have just the column to do that component_property_id
. I would also recommend using CONCAT()
so you don't need any type conversions:
SELECT component_module_id as component_module_id,
component_property_id,
CONCAT('cf',
ROW_NUMBER() OVER (PARTITION BY component_module_id ORDER BY component_property_id)
0 AS cf
FROM component_property
I should also note that your code uses VARCHAR
with no length. This is a really bad idea in SQL Server. The default length varies by context and may not be big enough for certain values (although you don't have that problem in this case).
Upvotes: 4
Reputation: 2760
Try this
SELECT component_module_id,
component_property_id,
'cf' + CAST(cf) AS VARCHAR(10)
FROM (
SELECT component_module_id,
component_property_id,
ROW_NUMBER() OVER (PARTITION BY component_module_id ORDER BY component_module_id) AS cf
FROM component_property
) a
ORDER BY component_module_id
The behaviour you are experiencing is becauseVARCHAR
orders numbers by doing an alphabetical sort
You need to do the ordering using an INT
and then do your concatenation at the end (by wrapping the original query in a sub-query
However, a better solution was suggested in the comments which is to only store numbers as INT
and add the prefix using a computed column (or at the query level)
Upvotes: 0
Reputation: 4061
If you do it this way, it will work
SELECT * FROM #t ORDER BY component_module_id, cast(replace(cf, 'cf', '') as int)
Upvotes: 1