1

Suppose you have a table with non-unique values such as this:

CREATE TABLE accounts ( fname VARCHAR(20), lname VARCHAR(20)) 
GO 

INSERT accounts VALUES ('Fred', 'Flintstone') 
INSERT accounts VALUES ('Fred', 'Flintstone') 
INSERT accounts VALUES ('Fred', 'Flintstone') 

SELECT * FROM accounts 
GO

Now using a ROW_NUMBER function, you can get a unique incrementing row number.

select *, ROW_NUMBER() over(order by (select null)) as rn
from accounts

But how do we this without using a ROW_NUMBER function. I tried giving each row a unique ID using NEWID() and then counting the rows as given below but it did not work as it gives me a non-unique number which does not start with 1. Note that I do not want to alter the table to add a new column.

;with cte as
(select *
from accounts as e
cross apply (select newid()) as a(id)
)
select *, (select count(*)+1 from cte as c1 where c.id > c1.id) as rn
from cte as c
order by rn

SQL Fiddle for toying around is http://sqlfiddle.com/#!18/c270f/3/0

13
  • 6
    Fortunately, we have row_number(). Commented Feb 16, 2018 at 20:15
  • 3
    yeah. What's wrong with row_number()? It's the best thing invented for this thing. Commented Feb 16, 2018 at 20:31
  • 1
    What is wrong with row_number()? It has been around since SQL 2005. You want to use a cursor? Commented Feb 16, 2018 at 21:24
  • 1
    Nothing wrong. But trying to understand set based solution/pattern that will get me the answer without using Window function, cursors, etc. Kind of what you would do before window function were introduced. Anyway, I still don't understand why using NEWID() did not work. Commented Feb 16, 2018 at 22:01
  • 1
    @Paparazzi: window functions have been defined in SQL:2003, there never was a "SQL 2005" standard. Commented Feb 17, 2018 at 7:12

4 Answers 4

2

The following demonstrates why your code fails, but does not provide an alternative to Row_Number().

A column, TopId, is added to the final select that should get the minimum value generated by NewId() and report it in every row. Instead, a new value is generated for each row.

-- Sample data.
declare @Samples as Table ( FName VarChar(20), LName VarChar(20) );
insert into @Samples ( FName, LName ) values
  ( 'Fred', 'Flintstone' ), ( 'Fred', 'Flintstone' ), ( 'Fred', 'Flintstone' );
select * from @Samples;

-- Cross apply   NewId()   in a CTE.
;with cte as
  ( select *
      from @Samples as S
    cross apply ( select NewId() ) as Ph( Id ) )
  select *, ( select count(*) from cte as c1 where c1.Id >= c.Id ) as RN,
    -- The following column should output the minimum   Id   value from the table for every row.
    --   Instead, it generates a new unique identifier for each row.
    ( select top 1 id from cte order by id ) as TopId
    from cte as c
    order by RN;

The execution plan shows that the CTE is treated as a view that is being evaluated repeatedly, thus generating conflicting Id values.

Sign up to request clarification or add additional context in comments.

1 Comment

That explains it!
1

How about this:

SELECT 
  src.*, 
  SUM(DummyVal) OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RowId
FROM (
  SELECT a.*, 1 AS DummyVal
  FROM MyTable a
) src

It's still a window function, though, not sure if that matters.

Fiddle me this

1 Comment

This approach is useful when you want to number rows according to the order by clause of the query. Otherwise you have to write the same order by clause inside "row_number over(..)".
0

You can create an function yourself to compute the row_number,

In this example, I had to calculate an index for a lesson within a course.

Window Function version:

 SELECT *,  ROW_NUMBER() OVER(PARTITION BY courseId) AS row_num FROM lessons;

I created helper-function, to compute the row_number without window function:

DELIMITER $$
CREATE FUNCTION getRowNumber (lessonId int, courseId int) 
RETURNS int
DETERMINISTIC
BEGIN
  DECLARE count int;
select count(l2.id) into count  from lessons l2 where l2.courseId=courseId
and l2.id<=lessonId;
RETURN count;
END$$
DELIMITER ;

so, the final query is:

 SELECT l.*,  getRowNumber(l.id,l.courseId) as row_num  FROM lessons l;

got the same result as the first query!

Comments

-2

MySQL:

SELECT @rownum := @rownum + 1 AS rank, a.* 
FROM accounts a,(SELECT @rownum := 0) r;

In ORACLE it would simply be

SELECT ROWNUM, a.*
FROM accounts a;

Both without window

3 Comments

OP specifically put sql-server tag on the question. What's the purpose of giving answer on other dbms???
it was not there in the beginning as far as I recall
My initial tags were 'T-SQL' and 'sql' as I was looking for either a standard sql set based answer or specific to sql server. I changed them to 'T-SQL' and 'sql server' to avoid confusion.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.