ssdt: change the types of primary key/secondary keys - sql-server-data-tools

Original table1 and Table2. Both tables has data.
CREATE TABLE [dbo].[Table1]
(
[Id] int NOT NULL PRIMARY KEY
)
CREATE TABLE [dbo].[Table2]
(
[Id] INT NOT NULL PRIMARY KEY,
[Table1Id] Int NULL,
Constraint [FK_Table1_Table2] foreign key ([Table1Id]) references [Table1] (Id)
)
I'd like to change the Table1.Id to UNIQUEIDENTIFIER.
Obviously just jump in and change the type from int to UNIQUEIDENTIFIER for Table1.Id and 'Table2.Table1Id'. Then Publish. Here is the code:
CREATE TABLE [dbo].[tmp_ms_xx_Table1] (
[Id] UNIQUEIDENTIFIER NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
IF EXISTS (SELECT TOP 1 1
FROM [dbo].[Table1])
BEGIN
INSERT INTO [dbo].[tmp_ms_xx_Table1] ([Id])
SELECT [Id]
FROM [dbo].[Table1]
ORDER BY [Id] ASC;
END
This code will fail because original Table1.Id is Int while temp table Id is UNIQUEIDENTIFIER.
Then, I try to with Pre-Scripts. Ideally all the changes will be done manually.
--drop fk constraint
alter table [Table2] drop constraint [FK_Table1_Table2];
--rename table1.id
exec sp_rename 'Table1.Id', 'Id2', 'COLUMN';
alter table [Table1] add Id uniqueidentifier not null
default newid();
--rename table2.table1id
exec sp_rename 'Table2.Table1Id', 'Table1Id2', 'COLUMN';
alter table [Table2] add Table1Id uniqueidentifier null;
update t2 set t2.Table1ID = t1.Id
from Table2 t2 left join Table1 t1 on t2.Table1Id2 = t1.Id2;
alter table [Table2] add constraint [FK_Table1_Table2] foreign key (Table1Id) references Table1 (Id);
However it FAIL again as SSDT is trying to compare its data structure again the target database.
Any idea please?

You're right. The problem is that you can't include schema changes in the pre-deployment script because SSDT's deployment script is generated prior to your schema changes. It is therefore only useful for data-only changes.
The solution is to do this outside of the SSDT process altogether. Yes, it's a pre-pre-deployment script! Essentially you have to apply your change by yourself before you even get to the SSDT bit.
(There's probably a way to do this via a custom deployment contributor. After all, everything is possible in code...)
Can I convince you to take a look at a migration-based solution as it appears that you have sufficient need for an element of fine-grained script "customisation". DBUp is a popular open source solution. ReadyRoll is a more-integrated commercial solution that shares a lot with SSDT.

the problem is the old data. it will be ok without the data in table in step 2.
1.pre-script: copy/process old data to temp tables, delete them from original tables
create table #table1 (
id int null,
id2 uniqueidentifier null
);
insert into #table1 (id,id2)
select id,newid() from Table1;
create table #table2 (
id int null,
table1id int null,
table1id2 uniqueidentifier null
);
insert into #table2 (id, table1id)
select id,table1id from Table2;
update t2 set t2.table1id2=t1.id2
from #table2 t2 left join #table1 t1 on t2.table1id = t1.id;
delete from table2;
delete from table1;
dacpac will auto generate the changes for schema. it will be ok because no data is existing any more.
post-script: insert data back from temp tables in pre-script:
insert into table1 (id)
select id2 from #table1;
insert into table2 (id,table1id)
select id, table1id2 from #table2;

Related

Insert the id from a previous query inside a sqlite trigger

In a sqlite3 database I would like to create a trigger on a view so that I can insert data over the view. Inside the trigger I would like to insert something in the tblmedia table. The id of the inserted row should be now also inserted into the tblbook as id.
In sqlite there are no variables. Otherwise I would store the returning value in the variable and would use it in the second query.
Can this even be achieved in sqlite?
Following my sql schema:
CREATE TABLE tblmedia(
id INTEGER PRIMARY KEY NOT NULL,
title VARCHAR NOT NULL,
raiting INTEGER,
file_name VARCHAR NOT NULL,
media_type TEXT NOT NULL
);
CREATE TABLE tblbook(
id INTEGER PRIMARY KEY NOT NULL,
author VARCHAR,
FOREIGN KEY (id) REFERENCES tblmedia(id) ON DELETE CASCADE
);
CREATE VIEW book AS
SELECT
m.id as id,
m.title as title,
b.author as author,
m.raiting as raiting,
m.file_name as file_name
FROM tblbook b
LEFT JOIN tblmedia m ON m.id = b.id;
CREATE TRIGGER insert_book
INSTEAD OF INSERT ON book
BEGIN
INSERT INTO tblmedia(title, raiting, file_name)
VALUES(new.title, new.raiting, new.file_name);
INSERT INTO tblbook(id, author)
VALUES (xx, new.author); -- xx should be the id from the previous insert
END

SQLite Order By foreign table field

I have two tables like following: (They have much more fields in reality)
Records (
recordId INTEGER PRIMARY KEY,
dateTime TEXT
);
Alarms (
alarmId INTEGER PRIMARY KEY,
recordId INTEGER,
FOREIGN KEY (recordId) REFERENCES Records(recordId)
ON UPDATE CASCADE
ON DELETE CASCADE
);
I want to get all Alarms sorted by dateTime of Record they are referring. I think it should be something like this:
SELECT * FROM Alarms ORDER BY Records.dateTime DESC;
But I couldn't find any relatable example.
Thank you for your time.
What you need is a join of the 2 tables:
SELECT a.*
FROM Alarms a INNER JOIN Records r
ON r.recordId = a.recordId
ORDER BY r.dateTime DESC;
Or a correlated subquery in the ORDER BY clause:
SELECT a.*
FROM Alarms a
ORDER BY (SELECT r.dateTime FROM Records r WHERE r.recordId = a.recordId) DESC;

SQLite insert into table using the id from an inner insert

I'm trying to insert a parent and child at the same time.
My idea is to insert the parent, get the id using SELECT last_insert_rowid() AS [Id] and use this id to insert the child
I can get each part of this working independently but not as a whole. This is what I currently have:
INSERT INTO ParentTable (Col1)
VALUES( 'test')
SELECT last_insert_rowid() AS [Id]
The above works - so far so good. Now I want to use the result of this in the child insert. This is what I have:
INSERT INTO ChildTable (col1, col2, ParentId)
VALUES( 1, 2, SELECT Id FROM (
INSERT INTO ParentTable (Col1)
VALUES( 'test')
SELECT last_insert_rowid() AS [Id]
);
I get this error:
near "SELECT": syntax error:
Can anyone point me in the right direction?
You can't use INSERT in SELECT statement. You should first insert and then use last inserted id:
INSERT INTO ParentTable (Col1) VALUES( 'test');
INSERT INTO ChildTable (col1, col2, ParentId)
VALUES(1,2, (SELECT last_insert_rowid()));
Since you want to insert many records with parent ID, here is a workaround:
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE IF NOT EXISTS temp(id integer);
DELETE FROM temp;
INSERT INTO ParentTable (Col1) VALUES( 'test');
INSERT INTO temp SELECT last_insert_rowid();
INSERT INTO ChildTable (col1, col2, ParentId)
VALUES(1,2, (SELECT id FROM temp LIMIT 1));
.............
COMMIT;
DROP TABLE temp;
Or you can create a permanent table to this effect.
That SQLite.Net PCL driver assumes that you use the ORM: inserting an object will automatically read back and assign the autoincremented ID value.
If you're using raw SQL, you have to manage the last_insert_rowid() calls yourself.
Your idea is correct, but you have to do everything in separate SQL statements:
BEGIN; -- better use RunInTransaction()
INSERT INTO Parent ...;
SELECT last_insert_rowid(); --> store in a variable in your program
INSERT INTO Child ...;
...
END;
(SQLite is an embedded database and has no client/server communication overhead; there is no reason to try to squeeze everything into a single statement.)

Except the name of table what property can use to differentiate these tables

I have a database which contains many tables and these tables can be added or removedd any time.So I give each of them a different name like Table1,Table2,...
but it's uncomfortable to use these table because sometime I forget what infomation was stored in Table1
So I want something to differentiate these all tables, some property that I can be specified when I create a table and I can use to access a specific table when I need to fetch informations from that table
As one comment says, you could create a table for notes on the other tables:
CREATE TABLE notes (
id INT AUTO_INCREMENT,
table_name VARCHAR(64),
note VARCHAR(255),
PRIMARY KEY (id)
);
By the way, MySQL (but not SQLite) allows comments on the table itself:
CREATE TABLE table1 (
id INT AUTO_INCREMENT,
val INT,
PRIMARY KEY (id)
) COMMENT = 'Table of stuff';
-- Show the comment
SHOW TABLE STATUS WHERE NAME='table1';
-- Just show names and comments
SELECT `TABLE_NAME`, `TABLE_COMMENT`
FROM information_schema.tables
WHERE table_schema = DATABASE();

problem with sql table updation

I have 2 tables
CREATE TABLE PODRAS_MS.tbl_FieldWorkers
(
[FWInsOnServerID] INT NOT NULL IDENTITY(1,1),
[BaseStationID] INT NOT NULL,
[RefID] INT NOT NULL,
[DisSubInsID] INT NOT NULL,
[FieldWorkerID] CHAR(7) NOT NULL,
[LastRecDate] DATETIME NOT NULL,
)
ALTER TABLE PODRAS_MS.tbl_FieldWorkers ADD CONSTRAINT PK_FieldWorkers_FWInsOnServerID PRIMARY KEY([FWInsOnServerID])
ALTER TABLE PODRAS_MS.tbl_FieldWorkers ADD CONSTRAINT FK_FieldWorkers_DisSubInsID FOREIGN KEY([DisSubInsID]) REFERENCES PODRAS_MS.tbl_DisasterSubInstances([SubInsID]) ON UPDATE CASCADE ON DELETE NO ACTION
ALTER TABLE PODRAS_MS.tbl_FieldWorkers ADD CONSTRAINT FK_FieldWorkers_BaseStationID FOREIGN KEY([BaseStationID]) REFERENCES PODRAS_MS.tbl_BaseStations([BaseStationID]) ON UPDATE CASCADE ON DELETE NO ACTION
ALTER TABLE PODRAS_MS.tbl_FieldWorkers ADD CONSTRAINT DF_FieldWorkers_LastRecDate DEFAULT(GETDATE()) FOR [LastRecDate]
GO
CREATE TABLE PODRAS_MS.tbl_FieldWorkerNodeGPSLocations
(
[FWNStatID] INT NOT NULL IDENTITY(1,1),
[FWInsOnServerID] INT NOT NULL,
[Latitude] DECIMAL(20,17) NOT NULL,
[Longitude] DECIMAL(20,17) NOT NULL,
[UpdateOn] DATETIME NOT NULL,
)
ALTER TABLE PODRAS_MS.tbl_FieldWorkerNodeGPSLocations ADD CONSTRAINT PK_FieldWorkerNodeGPSLocations_FWNStatID PRIMARY KEY([FWNStatID])
ALTER TABLE PODRAS_MS.tbl_FieldWorkerNodeGPSLocations ADD CONSTRAINT FK_FieldWorkerNodeGPSLocations_FWInsOnServerID FOREIGN KEY([FWInsOnServerID]) REFERENCES PODRAS_MS.tbl_FieldWorkers([FWInsOnServerID]) ON UPDATE CASCADE ON DELETE NO ACTION
ALTER TABLE PODRAS_MS.tbl_FieldWorkerNodeGPSLocations ADD CONSTRAINT DK_FieldWorkerNodeGPSLocations_UpdateOn DEFAULT(GETDATE()) FOR [UpdateOn]
GO
Both tables are updated through a webservice in the 1st table all the fields can be inserted through the web service but in the second table only data for [Latitude],[Longitude],[UpdateOn] fields comes through the webservice.so my problem is how can i insert the values to [FWInsOnServerID] field since its not comes through the webservice and its a reference for the 1st table???
If I understand you correctly, the insert in the second table is dependant on the result of the insert in your first table?
In this case, you could simply return the generted ID of the first table and use that result to insert into the second table.
Something like (if you're using Stored Procedures).
SELECT SCOPE_IDENTITY()
at the end of your stored procedure. And then in your .NET code which handles the database code, use that number to do the second insert.
You could do it all in a single stored procedure like this:
CREATE PROCEDURE PODRAS_MS.insert_FieldWorker()
#BaseStationID INT,
#RefID INT,
#DisSubInsID INT,
#FieldWorkerID CHAR(7),
#Latitude DECIMAL(20,17),
#Longitude DECIMAL(20,17)
AS
BEGIN
INSERT INTO
PODRAS_MS.tbl_FieldWorkers
([BaseStationID], [RefID], [DisSubInsID], [FieldWorkerID])
VALUES (#BaseStationID, #RefID, #DisSubInsID, #FieldWorkerID)
DECLARE #FWInsOnServerID INT
SELECT #FWInsOnServerID = SCOPE_IDENTITY()
INSERT INTO PODRAS_MS.tbl_FieldWorkerNodeGPSLocations
([FWInsOnServerID], [Latitude], [Longitude])
VALUES (#FWInsOnServerID, #Latitude, #Longitude)
END
You could then select the records from the same stored procedure, but it is more common to separate this out into another stored proc.
EDIT: use an output parameter
CREATE PROCEDURE PODRAS_MS.insert_FieldWorker()
#BaseStationID INT,
#RefID INT,
#DisSubInsID INT,
#FieldWorkerID CHAR(7),
#FWInsOnServerID INT output
AS
BEGIN
INSERT INTO
PODRAS_MS.tbl_FieldWorkers
([BaseStationID], [RefID], [DisSubInsID], [FieldWorkerID])
VALUES (#BaseStationID, #RefID, #DisSubInsID, #FieldWorkerID)
SELECT #FWInsOnServerID = SCOPE_IDENTITY()
END

Resources