简体   繁体   中英

SQL Server - Using ROW_NUMBER() OVER PARTITION function to SET a value

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.

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 because VARCHAR 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)

如果你这样做,它会起作用

SELECT * FROM #t ORDER BY component_module_id, cast(replace(cf, 'cf', '') as int)

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).

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM