Function to generate incrementing numbers in SQL Server - sql-server

Is there any way to select from a function and have it return incrementing numbers?
For example, do this:
SELECT SomeColumn, IncrementingNumbersFunction() FROM SomeTable
And have it return:
SomeColumn | IncrementingNumbers
--------------------------------
some text | 0
something | 1
foo | 2

On sql server 2005 and up you can use ROW_NUMBER()
SELECT SomeColumn,
ROW_NUMBER() OVER(Order by SomeColumn) as IncrementingNumbers
FROM SomeTable
0n SQL Server 2000, you can use identity but if you have deletes you will have gaps
SQL 2000 code in case you have gaps in your regular table with an identity column, do an insert into a temp with identity and then select out of it
SELECT SomeColumn,
IDENTITY( INT,1,1) AS IncrementingNumbers
INTO #temp
FROM SomeTable
ORDER BY SomeColumn
SELECT * FROM #temp
ORDER BY IncrementingNumbers

I think you're looking for ROW_NUMBER added in SQL Server 2005. it "returns the sequential number of a row within a partition of a result set, starting at 1 for the first row in each partition."
From MSDN (where there's plenty more) the following example returns the ROW_NUMBER for the salespeople in AdventureWorks2008R2 based on the year-to-date sales.
SELECT FirstName, LastName, ROW_NUMBER() OVER(ORDER BY SalesYTD DESC) AS 'Row Number', SalesYTD, PostalCode
FROM Sales.vSalesPerson
WHERE TerritoryName IS NOT NULL AND SalesYTD <> 0;

You could an auto increment identity column, or do I miss understand the question?
http://msdn.microsoft.com/en-us/library/Aa933196

Starting with SQL 2012 versions and later, there is a built-in sequence feature.
Example:
-- sql to create the Sequence object
CREATE SEQUENCE dbo.MySequence
AS int
START WITH 1
INCREMENT BY 1 ;
GO
-- use the sequence
SELECT NEXT VALUE FOR dbo.MySequence;
SELECT NEXT VALUE FOR dbo.MySequence;
SELECT NEXT VALUE FOR dbo.MySequence;

No, there is no sequence generation functions in SQLServer. You might find identity field types handy to resolve your current issue.

Related

SQL duplicates with percentage result

I have an issue where my database contains a table with these columns:
Program_ID, Vehicle_VIN, Vehicle_Type
I need to create a report where will be:
Program_ID, AmountOfAllPoliciesInProgram, PercentageDuplicatesInProgram
where Vehicle_type = 10
and criteria for the duplicate is to have Vehicle_VIN more than 1 unique time in the table for dedicated Program_ID.
It's in Microsoft SQL Server Management Studio
AmountOfAllPoliciesInProgram is:
SELECT
PROGRAM_ID, COUNT(*) AS AmountOfAllPoliciesInProgram
FROM
dbo.table
WHERE
Vehicle_type = 10
GROUP BY
PROGRAM_ID
I'm not 100% sure I understand you correctly, but you can count distinct Vehicle_VIN and use the having clause to test if the count of distinct Vehicle_VIN is larger than 1. Like so,
SELECT PROGRAM_ID, COUNT(*) as AmountOfAllPoliciesInProgram, count(distinct Vehicle_VIN) as VIN_COUNT
FROM dbo.table
where Vehicle_type = 10
group by PROGRAM_ID
having count(distinct Vehicle_VIN)>1

SQL Server : return id column using max on different column

In my table I have the columns id, userId and points. As a result of my query I would like to have the id of the record that contains the highest points, per user.
In my experience (more with MySQL than SQL Server) I would use the following query to get this result:
SELECT id, userId, max(points)
FROM table
GROUP BY userId
But SQL Server does not allow this, because all columns in the select should also be in the GROUP BY or be an aggregate function.
This is a hypothetical situation. My actual situation is a lot more complicated!
Use ROW_NUMBER window function in SQL Server
Select * from
(
select Row_Number() over(partition by userId Order by points desc) Rn,*
From yourtable
) A
Where Rn = 1

Group by working in SQL Server 2000 but not in SQL Server 2005

I used following query in SQL Server 2000
SELECT
U.FirstName,
SUM(VE.Score)AS Score, SUM(VE.QuizTime) AS Time,
SUM(VE.IsQuizType) AS QuizesAttempted,
SUM(VE.IsProgrammingType) AS ProgrammingProblemsAttempted
from
Users U INNER JOIN VirtualExercise VE on U.UserID=VE.UserID
where U.UserID IN( 10,11 ) AND ProgramID = 2
group by U.FirstName
order by VE.Score desc
It working fine in SQL Server 2000 but not working in SQL Server 2005.
Gives following error:
Column "VirtualExercise.Score" is
invalid in the ORDER BY clause because
it is not contained in either an
aggregate function or the GROUP BY
clause. --- Inner Exception
Please help...
SQL Server 2005 is correct: you are trying to order by a value that doesn't exist in the output because you must either GROUP on it or aggregate it. VE.Score is neither. SQL Server 2000 allowed some ambiguities like this (edit: see "ORDER BY clause" at http://msdn.microsoft.com/en-us/library/ms143359(SQL.90).aspx for info on this)
I assume you mean
ORDER BY SUM(VE.Score) DESC
SQL Server 2000 is kind of dumb where it comes to table and column aliases in the result set. Provided an explicit alias column is introduced (e.g. Score in your case, Col3 in the example below), it doesn't matter what table alias you use in the ORDER BY clause:
create table #T1 (
ID int not null primary key,
Col1 varchar(10) not null
)
go
create table #T2 (
ID int not null primary key,
Col2 varchar(10) not null
)
go
insert into #T1 (ID,Col1)
select 1,'abc' union all
select 2,'def'
go
insert into #T2 (ID,Col2)
select 1,'zyx' union all
select 2,'wvu'
go
select *,1 as Col3 from #T1 t1 inner join #T2 t2 on t1.ID = t2.ID order by t2.Col3
Since you presumably want to sort by the computed Score column, just remove the VE. prefix.
Since you have defined SUM(VE.Score)AS Score in select , do you mean ORDER BY Score DESC

Update SQL with consecutive numbering

I want to update a table with consecutive numbering starting with 1. The update has a where clause so only results that meet the clause will be renumbered. Can I accomplish this efficiently without using a temp table?
This probably depends on your database, but here is a solution for MySQL 5 that involves using a variable:
SET #a:=0;
UPDATE table SET field=#a:=#a+1 WHERE whatever='whatever' ORDER BY field2,field3
You should probably edit your question and indicate which database you're using however.
Edit: I found a solution utilizing T-SQL for SQL Server. It's very similar to the MySQL method:
DECLARE #myVar int
SET #myVar = 0
UPDATE
myTable
SET
#myvar = myField = #myVar + 1
For Microsoft SQL Server 2005/2008. ROW_NUMBER() function was added in 2005.
; with T as (select ROW_NUMBER() over (order by ColumnToOrderBy) as RN
, ColumnToHoldConsecutiveNumber from TableToUpdate
where ...)
update T
set ColumnToHoldConsecutiveNumber = RN
EDIT: For SQL Server 2000:
declare #RN int
set #RN = 0
Update T
set ColumnToHoldConsecutiveNubmer = #RN
, #RN = #RN + 1
where ...
NOTE: When I tested the increment of #RN appeared to happen prior to setting the the column to #RN, so the above gives numbers starting at 1.
EDIT: I just noticed that is appears you want to create multiple sequential numbers within the table. Depending on the requirements, you may be able to do this in a single pass with SQL Server 2005/2008, by adding partition by to the over clause:
; with T as (select ROW_NUMBER()
over (partition by Client, City order by ColumnToOrderBy) as RN
, ColumnToHoldConsecutiveNumber from TableToUpdate)
update T
set ColumnToHoldConsecutiveNumber = RN
If you want to create a new PrimaryKey column, use just this:
ALTER TABLE accounts ADD id INT IDENTITY(1,1)
As well as using a CTE or a WITH, it is also possible to use an update with a self-join to the same table:
UPDATE a
SET a.columnToBeSet = b.sequence
FROM tableXxx a
INNER JOIN
(
SELECT ROW_NUMBER() OVER ( ORDER BY columnX ) AS sequence, columnY, columnZ
FROM tableXxx
WHERE columnY = #groupId AND columnY = #lang2
) b ON b.columnY = a.columnY AND b.columnZ = a.columnZ
The derived table, alias b, is used to generated the sequence via the ROW_NUMBER() function together with some other columns which form a virtual primary key.
Typically, each row will require a unique sequence value.
The WHERE clause is optional and limits the update to those rows that satisfy the specified conditions.
The derived table is then joined to the same table, alias a, joining on the virtual primary key columns with the column to be updated set to the generated sequence.
In oracle this works:
update myTable set rowColum = rownum
where something = something else
http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/pseudocolumns009.htm#i1006297
To get the example by Shannon fully working I had to edit his answer:
; WITH CTE AS (
SELECT ROW_NUMBER() OVER (ORDER BY [NameOfField]) as RowNumber, t1.ID
FROM [ActualTableName] t1
)
UPDATE [ActualTableName]
SET Name = 'Depersonalised Name ' + CONVERT(varchar(255), RowNumber)
FROM CTE
WHERE CTE.Id = [ActualTableName].ID
as his answer was trying to update T, which in his case was the name of the Common Table Expression, and it throws an error.
UPDATE TableName
SET TableName.id = TableName.New_Id
FROM (
SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS New_Id
FROM TableName
) TableName
I've used this technique for years to populate ordinals and sequentially numbered columns. However I recently discovered an issue with it when running on SQL Server 2012. It would appear that internally the query engine is applying the update using multiple threads and the predicate portion of the UPDATE is not being handled in a thread-safe manner. To make it work again I had to reconfigure SQL Server's max degree of parallelism down to 1 core.
EXEC sp_configure 'show advanced options', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO
EXEC sp_configure 'max degree of parallelism', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO
DECLARE #id int
SET #id = -1
UPDATE dbo.mytable
SET #id = Ordinal = #id + 1
Without this you'll find that most sequential numbers are duplicated throughout the table.
One more way to achieve the desired result
1. Create a sequence object - (https://learn.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql?view=sql-server-ver16)
CREATE SEQUENCE dbo.mySeq
AS BIGINT
START WITH 1 -- up to you from what number you want to start cycling
INCREMENT BY 1 -- up to you how it will increment
MINVALUE 1
CYCLE
CACHE 100;
2. Update your records
UPDATE TableName
SET Col2 = NEXT VALUE FOR dbo.mySeq
WHERE ....some condition...
EDIT: To reset sequence to start from the 1 for the next time you use it
ALTER SEQUENCE dbo.mySeq RESTART WITH 1 -- or start with any value you need`
Join to a Numbers table? It involves an extra table, but it wouldn't be temporary -- you'd keep the numbers table around as a utility.
See http://web.archive.org/web/20150411042510/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-numbers-table.html
or
http://www.sqlservercentral.com/articles/Advanced+Querying/2547/
(the latter requires a free registration, but I find it to be a very good source of tips & techniques for MS SQL Server, and a lot is applicable to any SQL implementation).
It is possible, but only via some very complicated queries - basically you need a subquery that counts the number of records selected so far, and uses that as the sequence ID. I wrote something similar at one point - it worked, but it was a lot of pain.
To be honest, you'd be better off with a temporary table with an autoincrement field.

How to create RowNum column in SQL Server?

In Oracle we have "rownum".
What can I do in SQL Server?
In SQL Server 2005 (and 2008) you can use the ROW_NUMBER function, coupled with the OVER clause to determine the order in which the rows should be counted.
Update
Hmm. I don't actually know what the Oracle version does. If it's giving you a unique number per row (across the entire table), then I'm not sure there's a way to do that in SQL Server. SQL Server's ROW_NUMBER() only works for the rows returned in the current query.
If you have an id column, you can do this:
select a.*,
(select count(*) from mytable b where b.id <= a.id) as rownum
from mytable a
order by id;
Of course, this only works where you're able to order rownums in the same (or opposite) order as the order of the ids.
If you're selecting a proper subset of rows, of course you need to apply the same predicate to the whole select and to the subquery:
select a.*,
(select count(*) from table b where b.id <= a.id and b.foo = 'X') as rownum
from table a where a.foo = 'X'
order by id;
Obviously, this is not particularly efficient.
Based on my understanding, you'd need to use ranking functions and/or the TOP clause. The SQL Server features are specific, the Oracle one combines the 2 concepts.
The ranking function is simple: here is why you'd use TOP.
Note: you can't WHERE on ROWNUMBER directly...
'Orable:
select
column_1, column_2
from
table_1, table_2
where
field_3 = 'some value'
and rownum < 5
--MSSQL:
select top 4
column_1, column_2
from
table_1, table_2
where
field_3 = 'some value'

Resources