Reset or Update Row Position Integer in Database Table - sql

I am working on a stored procedure in SQL Server 2008 for resetting an integer column in a database table. This integer column stores or persists the display order of the item rows. Users are able to drag and drop items in a particular sort order and we persist that order in the database table using this "Order Rank Integer".
Display queries for items always append a "ORDER BY OrderRankInt" when retrieving data so the user sees the items in the order they previously specified.
The problem is that this integer column collects a lot of duplicate values after the table items are re-ordered a bit. Hence...
Table
--------
Name | OrderRankInt
a | 1
b | 2
c | 3
d | 4
e | 5
f | 6
After a lot of reordering by the user becomes....
Table
--------
Name | OrderRankInt
a | 1
b | 2
c | 2
d | 2
e | 2
f | 6
These duplicates are primarily because of insertions and user specified order numbers. We're not trying to prevent duplicate order ranks, but we'd like a way to 'Fix' the table on item inserts/modifies.
Is there a way I can reset the OrderRankInt column with a single UPDATE Query?
Or do I need to use a cursor? What would the syntax for that cursor look like?
Thanks,
Kervin
EDIT
Update with Remus Rusanu solution. Thanks!!
CREATE PROCEDURE EPC_FixTableOrder
#sectionId int = 0
AS
BEGIN
-- "Common Table Expression" to append a 'Row Number' to the table
WITH tempTable AS
(
SELECT OrderRankInt, ROW_NUMBER() OVER (ORDER BY OrderRankInt) AS rn
FROM dbo.[Table]
WHERE sectionId = #sectionId -- Fix for a specified section
)
UPDATE tempTable
SET OrderRankInt = rn; -- Set the Order number to the row number via CTE
END
GO

with cte as (
select OrderId, row_number() over (order by Name) as rn
from Table)
update cte
set OrderId = rn;
This doesn't account for any foreign key relationships, I hope you are taken care of those.

Fake it. Make the column nullable, set the values to NULL, alter it to be an autonumber, and then turn off autonumber and nullable.
(You could skip the nullable steps.)

Related

Updating uniqueidentifier column with same value for rows with matching column value

I need a little help. I have this (simplified) table:
ID
Title
Subtype
RelatedUniqueID
1
My Title 1
1
NULL
2
My Title 2
1
NULL
3
My Title 3
2
NULL
4
My Title 4
2
NULL
5
My Title 5
2
NULL
6
My Title 6
3
NULL
What I am trying to accomplish is generating the same uniqueidentifier for all rows having the same subtype.
So result would be this:
ID
Title
Subtype
RelatedUniqueID
1
My Title 1
1
439753d3-9103-4d0e-9dd0-569dc71fd6a3
2
My Title 2
1
439753d3-9103-4d0e-9dd0-569dc71fd6a3
3
My Title 3
2
d0f08203-1197-4cc7-91bb-c4ca34d7cb0a
4
My Title 4
2
d0f08203-1197-4cc7-91bb-c4ca34d7cb0a
5
My Title 5
2
d0f08203-1197-4cc7-91bb-c4ca34d7cb0a
6
My Title 6
3
055838c6-a814-4bd1-a859-63d4544bb449
Requirements
One query to update all rows at once
The actual table has many more rows with hundreds of subtypes, so manually building a query for each subtype is not an option
Using SQL Server 2017
Thanks for any assist.
Because newid() is applied per-row, you have to generate the values first, so this has to involve the use of a temporary or permanent table to store the correlated ID>Subtype value.
So first you need to generate the GUID values per Subtype :
with subtypes as (
select distinct subtype
from t
)
select Subtype, NewId() RelatedId into #Id
from subtypes
And then you can use an updatable CTE to apply these to your base table:
with r as (
select t.*, id.RelatedId
from #id id
join t on t.subtype=id.Subtype
)
update r
set relatedUniqueId=RelatedId
See example DB<>Fiddle
You can use an updatable CTE with a window function to get this data:
with r as (
select t.*,
RelatedId = first_value(newid()) over (partition by t.Subtype order by ID rows unbounded preceding)
from t
)
update r
set relatedUniqueId = RelatedId;
db<>fiddle
I warn though, that newid() is somewhat unpredictable in when it is calculated, so don't try messing about with a joined update (unless you pre-save the IDs like #Stu has done).
For example, see this fiddle, the IDs were calculated differently for every row.
I have found the single query solution.
Pre-requirement for this to work is that RelatedUniqueID must already contain random values. (e.g. set default field value to newid)
UPDATE TestTable SET ForeignUniqueID = TG.ForeignUniqueID FROM TestTable TG INNER JOIN TestTable ON TestTable.SubType = TG.SubType
Update
As Stu mentions in the comments, this solution might affect performance on large datasets. Please keep that in mind.

Create a procedure to merge data and avoid duplicates

I am trying to create a SQL Server merge procedure that would allow me to merge new entries in the data set and nullify duplicates in the table. Both tables of the same type. I am trying to perform a merge and avoid duplicates. The ID and Email will always be a one to one relation. However, the source table sometimes will send the same email with two different Ids. We want to keep only one record per person and nullify all the email for the invalid record. Initial thoughts are to join the source table with the target table on email and check which emails have two occurrences and nullify, but how could I put this in one procedure.
Table 1 and Table 2:
Id | Email | First | Last | Building | Date |....
Example of duplicate:
1 | tst#tst.com | ...
2 | tst#tst.com | ...
Needed output:
1 | tst#tst.com
2 | null
Procedure:
CREATE PROCEDURE mergingTwo #TableType
AS
BEGIN
MERGE [target]
USING [source] ON [target].Id = [source].Id OR [target].Email = [source].Email
WHEN MATCHED THEN
UPDATE
SET
WHEN NOT MATCHED BY TARGET THEN
INSERT
Can do the Merge First then nullify the e-mail in a Second update like
With cte as (select id, row_number() over (partition by e-mail order by id asc) n_row
From table_foo)
Update table_foo
Set email = null
From table_foo
Inner Join cte
On cte.id = table_foo.id
And cte.n_row > 1
Sounds like a job for a union (unless you really want those NULL entries).
SELECT Email FROM Table1
UNION
SELECT Email FROM Table2
;

How to sort the GUID or Unique identifer column in SQL

I want to sort the newID column by using ORDER BY, but when I try to order by the id is getting changed each and every time when I execute the query.
I have tired using the CAST operator for converting to VARCHAR and try to sort it. But it is not working.
declare #temp table
(
id int identity(1,1),
newID UNIQUEIDENTIFIER
)
insert into #temp
SELECT NEWID()
insert into #temp
SELECT NEWID()
insert into #temp
SELECT NEWID()
insert into #temp
SELECT NEWID()
select * from #temp
select * from #temp order by cast(newID as varchar(40)) asc
id newID
1 9653de71-34c2-4409-bcee-6809e170e197
2 3f3e7ab8-a516-4dd2-a04b-31feeac8fdea
3 1f1d38b8-3c31-4479-ba48-b71ce8525ea3
4 33f1e2b9-f4c3-4e57-9267-ff729a326318
id newID
3 1f1d38b8-3c31-4479-ba48-b71ce8525ea3
4 33f1e2b9-f4c3-4e57-9267-ff729a326318
2 3f3e7ab8-a516-4dd2-a04b-31feeac8fdea
1 9653de71-34c2-4409-bcee-6809e170e197
The second table also I need to get sorted same like the first table when using ORDER BY statement.
What I think you are trying to do:
I think you want to return your newID values, sorted in ascending order with an incrementing row number in the id column.
What I think you are misunderstanding:
The ID of a row does not need to be in any particular order, it just needs to be unique. If you are using the incrementing integer value of your id column as an identifier elsewhere in your solution, then you do not need to worry about the order. This is important because if you were to insert a new newID value that when sorted fell between two existing newID values, some of the id values would have to change to retain the ordering. This would break any relationships based on the id value.
It is important to note here that the int identity(1,1) value automatically increments (not always by 1) for each row as it is inserted. If you insert your data 'out of order' then the value will also be 'out of order'. I think you are misunderstanding what this functionality is for. In short, it doesn't do what you want it to.
You also can order a uniqueidentifier column as is. You will be getting an error because you have called the column newID, which is a reserved keyword within SQL Server. If you want to keep this name (which I suggest you don't), you will need to reference it within square brackets: order by [newID]. Bear in mind that the 'correct' ordering of a uniqueidentifier is not the same as the alphabetical ordering of the value you see on the screen, much like how the numeric ordering of 1, 2, 3, 10, 11, 12 is different to alphabetically ordering the same values as 1, 10, 11, 12, 2, 3.
How to actually get to your desired output:
If on the off chance you really do just want to get the row number of the newID value that is in your table, you can do this with the row_number windowed function:
declare #temp table(id int identity(1,1)
,newID UNIQUEIDENTIFIER
);
insert into #temp(newID) values
('9653de71-34c2-4409-bcee-6809e170e197')
,('3f3e7ab8-a516-4dd2-a04b-31feeac8fdea')
,('1f1d38b8-3c31-4479-ba48-b71ce8525ea3')
,('33f1e2b9-f4c3-4e57-9267-ff729a326318');
-- Showing the int identity, which increments as new rows are added
select *
from #temp
order by [newID];
-- Using the row_number function to generate the id value at runtime
select row_number() over (order by [newID]) as id
,[newID]
from #temp
order by [newID];
Outputs
Using int identity:
+----+--------------------------------------+
| id | newID |
+----+--------------------------------------+
| 2 | 3F3E7AB8-A516-4DD2-A04B-31FEEAC8FDEA |
| 1 | 9653DE71-34C2-4409-BCEE-6809E170E197 |
| 3 | 1F1D38B8-3C31-4479-BA48-B71CE8525EA3 |
| 4 | 33F1E2B9-F4C3-4E57-9267-FF729A326318 |
+----+--------------------------------------+
and using row_number:
+----+--------------------------------------+
| id | newID |
+----+--------------------------------------+
| 1 | 3F3E7AB8-A516-4DD2-A04B-31FEEAC8FDEA |
| 2 | 9653DE71-34C2-4409-BCEE-6809E170E197 |
| 3 | 1F1D38B8-3C31-4479-BA48-B71CE8525EA3 |
| 4 | 33F1E2B9-F4C3-4E57-9267-FF729A326318 |
+----+--------------------------------------+
NEWSEQUENTIALID() - This will not generate random sequence id for the GUID. We can use this one instead of Newid().
CREATE TABLE Product_A
(
ID uniqueidentifier default NEWSEQUENTIALID(),
productname int
)
Insert Into Product_A(productname) values(1)
Insert Into Product_A(productname) values(2)
Insert Into Product_A(productname) values(3)
Select * from Product_A
Select * from Product_A order by ID
I have used like this, but i want to use the newsequentialid in column wise not has default values in table creation. But it is not possible to use like that. Any suggestion to convert the newid() to newsequentialid because we can sort after the table insertion
Basically in our project we are using the GUID for each transaction, sometimes it can be null also. we are using has unique identifier for the column newid. after the inserting into a table, we want to get the same order has it was inserted in the table. But when we do order by for GUID column, it is randomly sorting. I want the same order as it was inserted in the table

How can I remove duplicate rows from a table but keeping the summation of values of a column

Suppose there is a table which has several identical rows. I can copy the distinct values by
SELECT DISTINCT * INTO DESTINATIONTABLE FROM SOURCETABLE
but if the table has a column named value and for the sake of simplicity its value is 1 for one particular item in that table. Now that row has another 9 duplicates. So the summation of the value column for that particular item is 10. Now I want to remove the 9 duplicates(or copy the distinct value as I mentioned) and for that item now the value should show 10 and not 1. How can this be achieved?
item| value
----+----------------
A | 1
A | 1
A | 1
A | 1
B | 1
B | 1
I want to show this as below
item| value
----+----------------
A | 4
B | 2
Thanks in advance
You can try to use SUM and group by
SELECT item,SUM(value) value
FROM T
GROUP BY item
SQLfiddle:http://sqlfiddle.com/#!18/fac26/1
[Results]:
| item | value |
|------|-------|
| A | 4 |
| B | 2 |
Broadly speaking, you can just us a sum and a GROUP BY clause.
Something like:
SELECT column1, SUM(column2) AS Count
FROM SOURCETABLE
GROUP BY column1
Here it is in action: Sum + Group By
Since your table probably isn't just two columns of data, here is a slightly more complex example showing how to do this to a larger table: SQL Fiddle
Note that I've selected my rows individually so that I can access the necessary data, rather than using
SELECT *
And I have achieved this result without the need for selecting data into another table.
EDIT 2:
Further to your comments, it sounds like you want to alter the actual data in your table rather than just querying it. There may be a more elegant way to do this, but a simple way use the above query to populate a temporary table, delete the contents of the existing table, then move all the data back. To do this in my existing example:
WITH MyQuery AS (
SELECT name, type, colour, price, SUM(number) AS number
FROM MyTable
GROUP BY name, type, colour, price
)
SELECT * INTO MyTable2 FROM MyQuery;
DELETE FROM MyTable;
INSERT INTO MyTable(name, type, colour, price, number)
SELECT * FROM MyTable2;
DROP TABLE MyTable2;
WARNING: If youre going to try this, please use a development environment first (i.e one you don't mind breaking!) to ensure it does exactly what you want it to do. It's imperative that your initial query captures ALL the data you want.
Here is the SQL Fiddle of this example in action: SQL Fiddle

Get all missing values between two limits in SQL table column

I am trying to assign ID numbers to records that are being inserted into an SQL Server 2005 database table. Since these records can be deleted, I would like these records to be assigned the first available ID in the table. For example, if I have the table below, I would like the next record to be entered at ID 4 as it is the first available.
| ID | Data |
| 1 | ... |
| 2 | ... |
| 3 | ... |
| 5 | ... |
The way that I would prefer this to be done is to build up a list of available ID's via an SQL query. From there, I can do all the checks within the code of my application.
So, in summary, I would like an SQL query that retrieves all available ID's between 1 and 99999 from a specific table column.
First build a table of all N IDs.
declare #allPossibleIds table (id integer)
declare #currentId integer
select #currentId = 1
while #currentId < 1000000
begin
insert into #allPossibleIds
select #currentId
select #currentId = #currentId+1
end
Then, left join that table to your real table. You can select MIN if you want, or you could limit your allPossibleIDs to be less than the max table id
select a.id
from #allPossibleIds a
left outer join YourTable t
on a.id = t.Id
where t.id is null
Don't go for identity,
Let me give you an easy option while i work on a proper one.
Store int from 1-999999 in a table say Insert_sequence.
try to write an Sp for insertion,
You can easly identify the min value that is present in your Insert_sequence and not in
your main table, store this value in a variable and insert the row with ID from variable..
Regards
Ashutosh Arya
You could also loop through the keys. And when you hit an empty one Select it and exit Loop.
DECLARE #intStart INT, #loop bit
SET #intStart = 1
SET #loop = 1
WHILE (#loop = 1)
BEGIN
IF NOT EXISTS(SELECT [Key] FROM [Table] Where [Key] = #intStart)
BEGIN
SELECT #intStart as 'FreeKey'
SET #loop = 0
END
SET #intStart = #intStart + 1
END
GO
From there you can use the key as you please. Setting a #intStop to limit the loop field would be no problem.
Why do you need a table from 1..999999 all information you need is in your source table. Here is a query which give you minimal ID to insert in gaps.
It works for all combinations:
(2,3,4,5) - > 1
(1,2,3,5) - > 4
(1,2,3,4) - > 5
SQLFiddle demo
select min(t1.id)+1 from
(
select id from t
union
select 0
)
t1
left join t as t2 on t1.id=t2.id-1
where t2.id is null
Many people use an auto-incrementing integer or long value for the Primary Key of their tables, and it is often called ID or MyEntityID or something similar. This column, since it's just an auto-incrementing integer, often has nothing to do with the data being stored itself.
These types of "primary keys" are called surrogate keys. They have no meaning. Many people like these types of IDs to be sequential because it is "aesthetically pleasing", but this is a waste of time and resources. The database could care less about which IDs are being used and which are not.
I would highly suggest you forget trying to do this and just leave the ID column auto-increment. You should also create an index on your table that is made up of those (subset of) columns that can uniquely identify each record in the table (and even consider using this index as your primary key index). In rare cases where you would need to use all columns to accomplish that, that is where an auto-incrementing primary key ID is extremely useful—because it may not be performant to create an index over all columns in the table. Even so, the database engine could care less about this ID (e.g. which ones are in use, are not in use, etc.).
Also consider that an integer-based ID has a maximum total of 4.2 BILLION IDs. It is quite unlikely that you'll exhaust the supply of integer-based IDs in any short amount of time, which further bolsters the argument for why this sort of thing is a waste of time and resources.