reverting alpha numeric identity - sql-server

I see this question has been asked numerous times with rather good results, however this one differs in that we want to revert to a normal auto increment ID after we have added alpha numberic IDs.
We have 3 databases that will eventually all combine into a single database. In order for our system to remain compliant we need the IDs to persist over the merge. So far we have managed to combine the data in an alphanumeric fashion where we prefix the record ID with the database source, such as IDs that came from DBAA is now AA## and DBBB is now BB##.
The question now though, is it possible to revert the varchar ID column back to an auto increment integer without adding more columns or creating functions in the back end? The idea is that the next entry in the new merged DB will be one higher than the highest of the 3 DBs, so if AA10 was highest then the next entry would be 11. (with no prefix and no function in the back end)
CREATE TABLE tableTest
(
colID varchar(50) PRIMARY KEY,
[desc] varchar(10) NOT NULL
)
ALTER TABLE tableTest ALTER COLUMN colID int NOT NULL IDENTITY(1,1)
of course as you can imagine that didn't work. Ultimately I just want to know if this is even possible to do without back end functions or additional columns.

So, to sum up Sean's and my comments - the answer is no.
This is impossible for many reasons.
You can manipulate the data in that column to create unique numeric values (that is providing your values are unique as is right now) - something like this will do the trick:
UPDATE tableTest
SET colId = REPLACE(REPLACE(REPLACE(colId, 'AA', ''), 'BB', ''), 'CC', '') +
CASE
WHEN colId LIKE 'AA%' THEN '1'
WHEN colId LIKE 'BB%' THEN '2'
WHEN colId LIKE 'CC%' THEN '3'
END
This way your current number part stays the most significant digits - so if your table contained values like
. AA1, BB5, CC7, AA30, BB12, it will now contain these values:
. 11, 52, 73, 301, 122 - as you can see, the "order" is preserved.
Another option is to add a new identity column, delete the current colID column, and rename the identity column to colID.
This can be achieved either by using ssms's table designer or by using sp_rename.
Note that if you are not using the table designer you will have to first drop the primary key constraint before you can drop the column.

Related

Bad design to compare to computed columns?

Using SQL Server I have a table with a computed column. That column concatenates 60 columns:
CREATE TABLE foo
(
Id INT NOT NULL,
PartNumber NVARCHAR(100),
field_1 INT NULL,
field_2 INT NULL,
-- and so forth
field_60 INT NULL,
-- and so forth up to field_60
)
ALTER TABLE foo
ADD RecordKey AS CONCAT (field_1, '-', field_2, '-', -- and so on up to 60
) PERSISTED
CREATE INDEX ix_foo_RecordKey ON dbo.foo (RecordKey);
Why I used a persisted column:
Not having the need to index 60 columns
To test to see if a current record exists by checking just one column
This table will contain no fewer than 20 million records. Adds/Inserts/updates happen a lot, and some binaries do tens of thousands of inserts/updates/deletes per run and we want these to be quick and live.
Currently we have C# code that manages records in table foo. It has a function which concatenates the same fields, in the same order, as the computed column. If a record with that same concatenated key already exists we might not insert, or we might insert but call other functions that we may not normally.
Is this a bad design? The big danger I see is if the code for any reason doesn't match the concatenation order of the computed column (if one is edited but not the other).
Rules/Requirements
We want to show records in JQGrid. We already have C# that can do so if the records come from a single table or view
We need the ability to check two records to verify if they both have the same values for all of the 60 columns
A better table design would be
parts table
-----------
id
partnumber
other_common_attributes_for_all_parts
attributes table
----------------
id
attribute_name
attribute_unit (if needed)
part_attributes table
---------------------
part_id (foreign key to parts)
attribute_id (foreign key to attributes)
attribute value
It looks complicated but due to proper indexing this is super fast even if part_attributes contain billions of records!

Updating identity column in SQL Server and setting the seed starting value

I have a table filled with data and one of the columns - TrackingNumber - is an integer value. I have gotten a request to change it to auto-increment and to have it start the identity seed at 1000000. I know that I cannot alter a column to be an identity column in an existing table with data, so I have two options: either create an entirely new table and then move data from the old table into that new table or add an new identity column and update it with data from the old column.
The problem is that I need to retain all the existing values in column TrackingNumber. I have tried the following:
1) ALTER TABLE [dbo].[Table1]
ADD [TrackingNumber2] [bigint] IDENTITY (1000000, 1) NOT NULL
2) UPDATE [dbo].[Table1]
SET [TrackingNumber2]=[TrackingNumber]
3) ALTER TABLE [dbo].[Table1]
DROP COLUMN [TrackingNumber]
GO
4) EXEC sp_rename 'Table1.TrackingNumber2', 'TrackingNumber','COLUMN'
I got an error on step 2 - Updating new column with the value from the old column: "Cannot update identity column 'TrackingNumber2'"
Can anyone recommend a workaround?
You just need to set identity_insert on for this table so you can update the values. Make sure you turn it back off when you complete the update. :)
https://msdn.microsoft.com/en-us/library/ms188059.aspx
Are you sure you need to use an identity column? There are alternatives. For example, since SQL Server 2012 (and azure, too), there are these things called sequences. You can define a sequence to start at any number you like:
create sequence dbo.TrackingSequence
as int
start with 1000000
increment by 1
no maxvalue
no cycle
no cache
Then, you can alter the table such that the default value for the column in question defaults from the sequence:
alter table dbo.MyTable
add constraint [MyTable.TrackingNumber.Default.TrackingSequence]
default( next value for dbo.TrackingSequence ) for TrackingNumber
(If the column already has a default value, you need to remove that first - in a separate statement.)
A sequence works a lot like an identity, but you don't have to disrupt existing values or the column definition per se.
The only trick is to remember to not specify the value for TrackingNumber, and let the DB do its thing.
Sequences are cool in that you can have one sequence that is used by multiple tables, giving you somewhat shorter db-wide unique IDs than alternatives in the past. For such an application, you'd probably be better off with a bigint column - or know that the tables in question aren't going to be terribly long.
I ended up creating a new table and moving data in there

SQL Server: Auto increment char pk

Is it possible to have a char primary key on a table? For example 'WC001' then will it automatically increment by 1, so the next record for the pk will be 'WC002' and so on.
Can anyone provide me example?
Thanks
Not directly - but you could have a normal INT IDENTITY auto-incrementing numerical ID and then defines a computed persisted column (SQL Server 2005 and newer) - something like:
CREATE TABLE dbo.YourTable
(ID INT IDENTITY(1,1),
CharID AS 'WC' + RIGHT('000' + CAST(ID AS VARCHAR(3)), 3) PERSISTED,
CONSTRAINT PK_YourTable PRIMARY KEY(CharID)
)
Inserting values into this table will cause the ID column to be 1, 2, 3, 4, 5, ..... and the CharID column will automatically be WC001, WC002, WC003 and so forth.
Since it's a persisted computed column, the values is always up to date, and you can even put an index (like the primary key) on it.
Not easily, but if you need something like this there's nothing stopping you from breaking up the alpha and numeric portions of your key. Make the WC portion AKey and the numeric be NKey, and auto-inc the Nkey.
If you want you can expose it in a view as:
SELECT AKey + CAST(nkey as varchar) as 'Key'
...
Implementing a "custom" identity never works out well since there are so many factors involved with resolving concurrency issues efficiently.
SQL Server 2012 will add support for more complicated identity fields.
My suggestion is to create another column named Id IDENTITY(1,1) INT and then make your desired column as computed column which will consist of Id and formatted number of 0s.

Adding a new primary key column

SQL SERVER, I'm working with a table that uses a guid for the key instead of an int but for the integration I'm working on, I need it to be an int. So, I want to write something that will create an ID column if it doesn't exist and populate it with the next highest ID. I'm not really sure how to do this though. Does anyone have code that does this?
i've tried this but the update doesn't work because they're null
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'c_Product'AND COLUMN_NAME = 'ProductId')
BEGIN
ALTER TABLE c_Product ADD ProductId INT
END
UPDATE c_Product SET ProductId = (SELECT Max(ProductId) + 1 END FROM c_Product)
Am I being dense here. Are you not just wanting to do:
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'c_Product'AND COLUMN_NAME = 'ProductId')
BEGIN
ALTER TABLE c_Product ADD ProductId INT IDENTITY(1,1) not null
END
Which will assign identity values for all existing rows, and then provide new numbers as new rows are inserted.
Ok, so you add a column called ProductId of type INT, which by the way is NULL. SQL Server stores values in one of three states. TRUE, FALSE, NULL. Now remember Null is not empty, it is not zero, it is not true and it is not false. It is pretty much that thing that never happens and doesnt exist. Get the point? Ok, so your row with this highest value in the 'newly' created column is ---- NULL. So what is the MAX(PorductId)? NULL. What is Null + 1? NULL. So, unless you set one value to 1 anywhere, you will be stuck in this never ending loop forever.
Back to table design. Using a GUID for a primary key, is a very bad no no! This will severely negatively impact the performance of your table. Unless you write an app that has to join over multiple servers, and platforms, STAY AWAY FROM USING GUIDS as the primary key. Long explanation, but basically all inserts are random, and then index page splits occur just about with every insert, and searches are slower too, and to use 16 bytes per row just for the PK column, not good. And then that is added to every other NC index, bad bad bad. If you can, change your table structure for good. Use and int.

Instead of trigger to update primary key

Long time reader, first time poster ;-)
I'm implementing a system based on an old system. The new system uses SQL Server 2008 and my problem comes when trying to insert new items in the main table. This will happen in two ways: It may be imported from the existing system (1) or may be created in the new system (2).
In case (1) the item already has an ID (int) which I would like to keep. In case (2) the ID will not be filled in and I'd like to generate an ID which is +1 of the maximum current value in the table. This should of course also work for inserts of mutiple rows.
As far as I can see, the solution will be to create a INSTEAD OF TRIGGER, but I can't quite figure out how this is done. Can anyone give me a hint or point me in the direction of how this can be done?
Chris
Following your request of using an INSTEAD OF trigger this SQL code can get you started.
CREATE TABLE dbo.SampleTable
(
ID INT,
SomeOtherValue VARCHAR(100) NULL
)
GO
CREATE TRIGGER dbo.TR_SampleTable_Insert
ON dbo.SampleTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
-- Inserting rows with IDs
INSERT INTO dbo.SampleTable (
ID,
SomeOtherValue)
SELECT
ID,
SomeOtherValue
FROM
Inserted
WHERE
ID IS NOT NULL
-- Now inserting rows without IDs
INSERT INTO dbo.SampleTable (
ID,
SomeOtherValue)
SELECT
(SELECT ISNULL(MAX(ID), 0) FROM dbo.SampleTable)
+ ROW_NUMBER() OVER(ORDER BY ID DESC),
SomeOtherValue
FROM
Inserted
WHERE
ID IS NULL
END
GO
INSERT INTO dbo.SampleTable
SELECT 1, 'First record with id'
UNION
SELECT NULL, 'First record without id'
UNION
SELECT 2, 'Second record with id'
UNION
SELECT NULL, 'Second record without id'
GO
SELECT * FROM dbo.SampleTable
GO
How about using a stored procedure to do your inserts, with the primary key as an optional parameter. In the stored procedure, you can set the primary key when it is not passed.
I would caution that if old and new records are being inserted mix and match, your scenario will probably fail, as new records will be getting old ID's before the old records are inserted. I recommend getting the max ID of the old table right now, and in the stored procedure setting the new primary key to be the Greater value of (old max + 1, current table max)
Others have shown you how to write such trigger.
Another, and often recommended, approach is to store both IDs in new database. Each record gets new ID in new system (by IDENTITY column or some other means). Additionally, if the record is imported from another system, it has associated OriginSystem and OriginID. This way you can keep old IDs for reference. This approach has additional benefit of being able to support new system to import data from, e.g. when merging or exchanging data with another system.

Resources