Create the first user in a table with circular references - sql

I'm creating a user-based database for a login system. I have around 15 tables in my DB. I have a procedure that add a new user in the DB. I had created a fake user(which send internal communication) before i added the circular references in my DB, from there i created my first real user with the reference to my user 0. Then I've deleted all the accounts to repopulate it.
My table is design like this:
ID, password, ..., idSupervisor (which is the circular references)
I cannot add a new account since I don't have the first fake accounts to add a a references.
How can I add the fake account again without dropping all the database
I'm working with SQL Server 2008
EDIT: my table is created that way:
CREATE TABLE User
(
ID INT PRIMARY KEY IDENTITY,
Pass VARCHAR(100) NOT NULL,
IdSupervisor INT,
FOREIGN KEY (IdSupervisor) REFERENCES User(ID) -- Actually added after in an alter table
)
I don't want to drop the whole database. I just want to add a new fake user so i can start my user list from there.

After a lengthy comment thread on the question, it would appear that the main problem is this error message:
Cannot insert the value NULL into column 'idSupervisor', table 'DB.dbo.User'; column does not allow nulls. INSERT fails.
Given another of your comments:
It could be null
It would seem that all you need to do is allow NULL values for that column. Something like this:
ALTER TABLE [User] ALTER COLUMN [IdSupervisor] INT NULL
Simply alter the column to allow NULL values, then you can insert a record with a NULL value in that column.

Related

How do I ensure that a referencing table also has data

My Postgres database has the following schema where the the user can store multi profile images.
CREATE TABLE users(
id INT GENERATE AS ALWAYS PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE images(
id INT GENERATE AS ALWAYS PRIMARY KEY,
url VARCHAR(50)
);
CREATE TABLE user_images(
user_id INT REFERENCES users(id),
image_id INT REFERENCES images(id)
);
How do I ensure that when I insert a user object, I also insert at least one user image?
You cannot do so very easily . . . and I wouldn't encourage you to enforce this. Why? The problem is a "chick and egg" problem. You cannot insert a row into users because there is no image. You cannot insert a row into user_images because there is no user_id.
Although you can handle this situation with transactions or delayed constraint checking, that covers only half the issue -- because you have to prevent deletion of the last image.
Here are two alternative.
First, you can simply add a main_image_id to the users table and insist that it be NOT NULL. Voila! At least one image is required.
Second, you can use a trigger to maintain a count of images in users. Then treat rows with no images as "deleted" so they are never seen.
When you insert a data into a table database can return a id from row which was inserted. So, if id > 0 the row has been inserted. But first, add column id (bigserial, auto increment, unique) to all tables.
INSERT INTO user_images VALUES (...) RETURNING id;

Can I use a trigger to create a column?

As an alternative to anti-patterns like Entity-Attribute-Value or Key-Value Pair tables, is it possible to dynamically add columns to a data table via an INSERT trigger on a parameter table?
Here would be my tables:
CREATE TABLE [Parameters]
(
id int NOT NULL
IDENTITY(1,1)
PRIMARY KEY,
Parameter varchar(200) NOT NULL,
Type varchar(200) NOT NULL
)
GO
CREATE TABLE [Data]
(
id int NOT NULL
IDENTITY(1,1)
PRIMARY KEY,
SerialNumber int NOT NULL
)
GO
And the trigger would then be placed on the parameter table, triggered by new parameters being added:
CREATE TRIGGER [TRG_Data_Insert]
ON [Parameters]
FOR INSERT
AS BEGIN
-- The trigger takes the newly inserted parameter
-- record and ADDs a column to the data table, using
-- the parameter name as the column name, the data type
-- as the column data type and makes the new column
-- nullable.
END
GO
This would allow my data mining application to get a list of parameters to mine and have a place to store that data once it mines it. It would also allow a user to add new parameters to mine dynamically, without having to mess with SQL.
Is this possible? And if so, how would you go about doing it?
I think the idea of dynamically adding columns will be a ticking time bomb, just gradually creeping towards one of the SQL Server limits.
You will also be putting the database design in the hands of your users, leaving you at the mercy of their naming conventions and crazy ideas.
So while it is possible, is it better than an EAV table, which is at least obvious to the next developer to pick up your program؟

SQL Server Management Studio Express crashes when I try to run an ALTER TABLE query to add a PK with auto increment

I have an existing table where I use existing column (type INT) as PK and manually increment its value with each row inserted. I wanted to change it to IDENTITY with auto increment. I found a thread here (http://stackoverflow.com/questions/4862385/sql-server-add-auto-increment-primary-key-to-existing-table) that seems to achieve exactly what I want. But every time I run the ALTER statement, Mgmt Studio crashes.
I had also tried to achieve my above goal by changing the column properties manually (Identity specification/Is Identity:yes) as in this thread (http://stackoverflow.com/questions/3876785/sql-server-cant-insert-null-into-primary-key-field). But every time I close the table after changing properties, I get an error
'Pix' table
Unable to modify table.
Cannot insert the value NULL into column 'picID', table 'photo.dbo.Tmp_Pix'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Not sure what's going on.
You cannot change an existing column to become an IDENTITY column.
What you need to do is:
create a new column with INT IDENTITY
drop the primary key constraint
drop the old column
add the primary key constraint on the new column
The trouble might be - if you already have data in that table - that the new identity values don't necessarily match the old values in your manual ID column.
If you need to preserve those, then it gets even more involved:
create a new table with the proper structure, and make sure that the ID column is INT IDENTITY
turn on IDENTITY_INSERT for that table
insert all the rows from the old table into the new one (and in the process, insert the old ID values into the new ID IDENTITY column)
turn off IDENTITY_INSERT for that table
drop the old table
possibly rename the new table

Update trigger old values natural key

I have an accounts table with the account owner as the primary key. In the update trigger, I want to update some accounts to new owners. Since this table doesn't have an id field, how do I use the inserted/updated tables in the trigger? DB is sql server 2008.
CREATE TRIGGER accounts_change_owner on accounts AFTER INSERT
AS BEGIN
MERGE INTO accounts t
USING
(
SELECT *
FROM inserted e
INNER JOIN deleted f ON
e.account_owner = f.account_owner ---this won't work since the new account owner value is diff
) d
ON (t.account_owner = d.account_owner)
WHEN MATCHED THEN
UPDATE SET t.account_owner = d.account_owner
END
I think I understood your question, but I am not sure. You want to be able update account owner name in one table and to have this update propagated to the referencing tables?
If so you don't really need a trigger, you can use on update cascade foreign key.
Like this:
create table AccountOwner
(
Name varchar(100) not null
constraint PK_AccountOwner primary key
)
create table Account
(
AccountName varchar(100) not null,
AccountOwnerName varchar(100) not null
constraint FK_Account_AccountOwnerName references AccountOwner(Name) on update cascade
)
insert AccountOwner values('Owner1')
insert Account values('Account1', 'Owner1')
Now if I update table AccountOwner like this
update AccountOwner
set Name = 'Owner2'
where Name = 'Owner1'
it will automatically update table 'Account'
select *
from Account
AccountName AccountOwnerName
----------- -----------------
Account1 Owner2
I think you need to modify the design of your table. Recall that the three attributes of a primary key are that the primary key must be
Non-null
Unique
Unchanging
(If the primary key consists of multiple columns, all columns must follow the rules above). Most databases enforce #1 and #2, but the enforcement of #3 is usually left up to the developers.
Changing a primary key value is a classic Bad Idea in a relational database. You can probably come up with a way to do it; that doesn't change the fact that it's a Bad Idea. Your best choice is to add an artificial primary key to your table, put NOT NULL and a UNIQUE constraints on the ACCOUNT_OWNER field (assuming that this is the case), and change any referencing tables to use the artificial key.
The next question is, "What's so bad about changing a primary key value?". Changing the primary key value alters the unique identifier for that particular data; if something else is counting on having the original value point back to a particular row, such as a foreign key relationship, after such a change the original value will no longer point where it's supposed to point.
Good luck.

Is there a smart way to append a number to an PK identity column in a Relational database w/o total catastrophe?

It's far from the ideal situation, but I need to fix a database by appending the number "1" to the PK Identiy column which has FK relations to four other tables. I'm basically making a four digit number a five digit number. I need to maintain the relations. I could store the number in a var, do a Set query and append the 1, and do that for each table...
Is there a better way of doing this?
You say you are using an identity data type for your primary key so before you update the numbers you will have to SET IDENTITY_INSERT ON (documentation here) and then turn it off again after the update.
As long as you have cascading updates set for your relations the other tables should be updated automatically.
EDIT: As it's not possible to change an identity value I guess you have to export the data, set the new identity values (+10000) and then import your data again.
Anyone have a better suggestion...
Consider adding another field to the PK instead of extending the length of the PK field. Your new field will have to cascade to the related tables, like a field length increase would, but you get to retain your original PK values.
My suggestion is:
Stop writing to the tables.
Copy the tables to new tables with the new PK.
Rename the old tables to backup names.
Rename the new tables to the original table name.
Count the rows in all the tables and double check your work.
Continue using the tables.
Changing a PK after the fact is not fun.
If the column in question has an identity property on it, it gets complicated. This is more-or-less how I'd do it:
Back up your database.
Put it in single user mode. You don't need anybody mucking around whilst you do the surgery.
Execute the ALTER TABLE statements necessary to
disable the primary key constraint on the table in question
disable all triggers on the table in question
disable all foreign key constraints referencing the table in question.
Clone your table, giving it a new name and a column-for-column identical definitions. Don't bother with any triggers, indices, foreign keys or other constraints. Omit the identity property from the table's definition.
Create a new 'map' table that will map your old id values to the new value:
create table dbo.pk_map
(
old_id int not null primary key clustered ,
new_id int not null unique nonclustered ,
)
Populate the map table:
insert dbo.pk_map
select old_id = old.id ,
new_id = f( old.id ) // f(x) is the desired transform
from dbo.tableInQuestion old
Populate your new table, giving the primary key column the new value:
insert dbo.tableInQuestion_NEW
select id = map.id ,
...
from dbo.tableInQuestion old
join dbo.pk_map map on map.old_id = old.id
Truncate the original table: TRUNCATE dbo.tableInQuestion. This should work—safely—since you've disabled all the triggers and foreign key constraints.
Execute SET IDENTITY_INSERT dbo.tableInQuestion ON.
Reload the original table:
insert dbo.tableInQuestion
select *
from dbo.tableInQuestion_NEW
Execute SET IDENTITY_INSERT dbo.tableInQuestion OFF
Execute drop table dbo.tableInQuestion_NEW. We're all done with it.
Execute DBCC CHECKIDENT( dbo.tableInQuestion , reseed ) to get the identity counter back in sync with the data in the table.
Now, use the map table to propagate the changed primary key column down the line. Depending on your E-R model, this can get complicated as foreign keys referencing the updated column may themselves be part of a composite primary key.
When you're all done, start re-enabling the constraints and triggers you disabled. Make sure you do this using the WITH CHECK option. Fix any problems thus uncovered.
Finally, drop the map table, and clear the single user flag and bring your system(s) back online.
Piece of cake! (or something.)
Consider this approach:
Reset the identity seed to the 10000 + the current seed.
Set identity insert on
Insert into the table from the values in the table and add 10000 to the identity column on the way.
EX:
Set identity insert on
Insert Table(identity, column1, eolumn2)
select identity + 10000, column1, column2
From Table
Where identity < origional max identity value
After the insert you know the identity is exactly 10000 more than the origional.
Update the foreign keys by addding 10000.