Azure SQL database duplicate the database schema - c#

I have an Azure SQL database that contains two tables Product and Order that are saved in the dbo schema. I want to write a C# program that will add new schema and duplicate the same tables definition that exist in dbo.
Existing:
dbo.Product
dbo.Order
Run the C# program to create a new schema for client1 then two tables will be added to the data base and the list of tables become:
dbo.Product
dbo.Order
client1.Product
client1.Order
Would you please let me know how I can do that with c#?

You can create a stored procedure that receives two input parameters:
name of the schema to be duplicated
name of the new schema
Then you can invoke this procedure from your c# program.
Inside the procedure you can build a dynamic statement querying SQLServer's system table INFORMATION_SCHEMA.TABLES.
The idea is to:
create the new schema
clone source tables from the old schema to the new schema
then delete all rows from the tables in the new schema
Please not that if you have foreign keys you'll have to manage them.
Here is a sample to start from:
create procedure p_clone_schema(
#source_schema nvarchar(50),
#new_schema nvarchar(50)
)as
begin
--check if destination schema already exists
if not exists (select * from INFORMATION_SCHEMA.SCHEMATA where schema_name = #new_schema)
begin
--create new schema
declare #sql nvarchar(max) = 'CREATE SCHEMA ' + QUOTENAME(#new_schema)
exec(#sql)
set #sql = ''
--build statement to copy tables in the new shema and then truncate them
select #sql = CONCAT
(
#sql , 'select * into ' , QUOTENAME(#new_schema) , '.' , QUOTENAME(TABLE_NAME)
, ' from ' , QUOTENAME(TABLE_SCHEMA) , '.' , QUOTENAME(TABLE_NAME)
, ' truncate table ' , QUOTENAME(#new_schema) , '.' , QUOTENAME(TABLE_NAME)
)
from INFORMATION_SCHEMA.TABLES
where TABLE_SCHEMA = #source_schema
and TABLE_TYPE = 'BASE TABLE'
exec(#sql)
end
end

Related

How to delete history from temporal tables via Entity Framework Core [duplicate]

I've recently discovered temporal tables in SQL Server. I'd like to start using this functionality. However the biggest hurdle is not being able to delete records from it. Due to GDPR compliance this is an absolute must.
Deleting records from a history table obviously leads to the error:
Cannot delete rows from a temporal history table
So to be able to delete records from a history table I have to disable SYSTEM_VERSIONING, then delete, and then re-enable SYSTEM_VERSIONING. Unless there's another way I'm not aware of?
Since it's not possible to use GO's in a stored procedure/SqlCommand, how can I ensure deleting a history record does not mess with other transactions e.g. updates sent to the temporal table during deleting records from the history table will still result in records being added to the history table?
I've tried creating a stored procedure to wrap it in one transaction but this fails because the ALTER TABLE statement disabling the SYSTEM_VERSIONING is not executed yet leading to the same error.
CREATE PROCEDURE [dbo].[OrderHistoryDelete]
(#Id UNIQUEIDENTIFIER)
AS
BEGIN
BEGIN TRANSACTION
ALTER TABLE [dbo].[Order] SET ( SYSTEM_VERSIONING = OFF )
-- No GO possible here obviously.
DELETE FROM [dbo].[OrderHistory] WITH (TABLOCKX)
WHERE [Id] = #Id
ALTER TABLE [dbo].[Order] SET ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[OrderHistory]))
COMMIT TRANSACTION
END
GO
If you make the DELETE dynamic, your stored procedure will successfully ALTER the table, DELETE the records in question, and then ALTER it back.
CREATE PROCEDURE [dbo].[OrderHistoryDelete]
(#Id UNIQUEIDENTIFIER)
AS
BEGIN
DECLARE #sql VARCHAR(MAX)
BEGIN TRANSACTION
ALTER TABLE [dbo].[Order] SET ( SYSTEM_VERSIONING = OFF )
SET #sql = 'DELETE FROM [dbo].[OrderHistory] WITH (TABLOCKX)
WHERE [Id] = ''' + CAST(#Id AS VARCHAR(40)) + ''''
EXEC (#sql)
ALTER TABLE [dbo].[Order] SET ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[OrderHistory]))
COMMIT TRANSACTION
END
I have never have issues like this, but my stored procedure is a little bit different - I am getting CSV of records to be deleted, then STRING_SPLIT them and materialized in a temporary table. Then, this table is joined to the target history table.
Here is full working example (only one input value). First create the table and add some sample data:
DROP TABLE IF EXISTS [dbo].[StackOverflow];
GO
CREATE TABLE [dbo].[StackOverflow]
(
[Col1] INT PRIMARY KEY
,[Col2] VARCHAR(32)
,[SysStartTime] DATETIME2 GENERATED ALWAYS AS ROW START HIDDEN NOT NULL
,[SysEndTime] DATETIME2 GENERATED ALWAYS AS ROW END HIDDEN NOT NULL
,PERIOD FOR SYSTEM_TIME ([SysStartTime],[SysEndTime])
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.StackOverflowChanges));
GO
INSERT INTO [dbo].[StackOverflow] ([Col1], [Col2])
VALUES (1, 'value 1')
,(2, 'value 2')
,(3, 'value 3');
GO
UPDATE [dbo].[StackOverflow]
SET [Col2] = [Col2] + ' v2'
GO
SELECT *
FROM [dbo].[StackOverflow];
SELECT *
FROM [dbo].[StackOverflowChanges];
GO
Then, create the stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER PROCEDURE [dbo].[usp_StackOverflow_Delete]
(
#Col1 INT
)
AS
BEGIN;
SET NOCOUNT, XACT_ABORT ON;
DROP TABLE IF EXISTS #RecordsTobeDeleted;
CREATE TABLE #RecordsTobeDeleted
(
[Col1] INT
);
INSERT INTO #RecordsTobeDeleted ([Col1])
VALUES (#Col1);
BEGIN TRY
BEGIN TRANSACTION;
ALTER TABLE [dbo].[StackOverflow] SET ( SYSTEM_VERSIONING = OFF )
DELETE [dbo].[StackOverflowChanges]
FROM [dbo].[StackOverflowChanges] SOC
INNER JOIN #RecordsTobeDeleted R
ON SOC.[Col1] = R.[Col1];
ALTER TABLE [dbo].[StackOverflow] SET ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[StackOverflowChanges]))
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN;
ROLLBACK TRANSACTION;
END;
THROW;
END CATCH
SET NOCOUNT, XACT_ABORT ON;
END;
GO
and now delete a record from the history table:
EXEC [dbo].[usp_StackOverflow_Delete] #Col1 = 1;
SELECT *
FROM [dbo].[StackOverflow];
SELECT *
FROM [dbo].[StackOverflowChanges];
Clean up:
ALTER TABLE [dbo].[StackOverflow] SET ( SYSTEM_VERSIONING = OFF )
DROP TABLE IF EXISTS [dbo].[StackOverflow];
DROP TABLE IF EXISTS [dbo].[StackOverflowChanges];

Unable to name table dynamically at creation - c#, sqlite

Using what's available in the System.Data.SQLite namespace and using C# I'm doing database operations. However, there is one problem I can't seem to get figured out.
If I want to create a table which is NAMED DYNAMICALLY AT CREATION, I thought the way to do that would be ...
command.CommandText = #"CREATE TABLE IF NOT EXISTS #tableName( ... )";
and then do this ...
command.Parameters.AddWithValue("#tableName", tableName);
and then do the
command.ExecuteNonQuery();
But what's happening is that I'm getting a new table named "#tableName" instead of whatever the variable tableName is set to.
How can I get this to work correctly?
The way that I approach problems like this is to simplify as much as I can. In this case, simple string substitution should easily resolve your problem without the need to use parameters.
For example:
command.Text = $#"CREATE TABLE IF NOT EXISTS {tableName}( ... )";
or
command.Text = string.Format(#"CREATE TABLE IF NOT EXISTS {0}( ... )", tableName);
Can you create procedure instead of inline query . you can manage easy in stored procedure like this .`CREATE PROCEDURE Dynamic_SP
#Table_Name sysname AS BEGIN
SET NOCOUNT ON;
DECLARE #DynamicSQL nvarchar(4000)
SET #DynamicSQL = N'SELECT * FROM ' + #Table_Name
EXECUTE sp_executesql #DynamicSQL END

How to get custom SQL Server type into Entity Framework generated code

There is a TABLE Type defined in SQL server:
CREATE TYPE RealtySearchResult AS TABLE
(
realtyId int not null,
OwnerId int not null,
...)
And stored procedure:
CREATE PROCEDURE [dbo].[SearchRealty]
(#fulltext nvarchar(200) null,
#skipRows int,
#pageCount int,
....
)
AS
BEGIN
DECLARE #SQL nvarchar(max)
DECLARE #result RealtySearchResult
CREATE TABLE #TEMP
(
realtyId int not null,
OwnerId int not null,
...
)
set #SQL = N'
INSERT INTO #TEMP
SELECT
realty.Id AS realtyId,
realty.OwnerId,
....join with fulltext catalog.... WHERE....#pageCount .....#skipRows'
-- sp_executesql cannot write to local table variable #result,
-- that is why it reads to temp table and then to #result
exec sp_executesql #SQL, N'#skipRows int, #pageCount int', #skipRows, #pageCount
INSERT INTO #result SELECT * FROM #TEMP
SELECT * FROM #result
END
And then in Visual Studio I update the model from database and a new method (wrapper for store procedure SearchRealty) is generated, but it does not contains generated code for returning complex type.
I would expect that EntityFramework should be able to recognize that the store procedure returns defined table type RealtySearchResult and should generate wrapper for it. I am too lazy to write the complex return type by myself in C# again (I just wrote it in SQL). It is really needed?
Can I just generate wrapper for RealtySearchResult type in EntityFramework somehow?
I use Visual Studio 2017 and EntityFramework 6.
It sounds as duplicate as Stored procedure in Entity Framework database first approach but once I click the button Get Column Information I got message "The selected stored procedure or function returns no columns".
Analysis
Based on link Entity Framework not getting column info on a different schema provided by kirsten I realize that EntityFramework execute stored procedure with mode
SET FMTONLY ON
It means it strips all condition and dynamic SQL. This result in empty temporary table and procedure failing during receiving metadata from EntityFramework.
Solution
To help the designer to get metadata without dynamic SQL. Count with that conditions are removed. Following code does a job:
DECLARE #result RealtySearchResult
IF 0=1
BEGIN
SELECT * FROM #result
RETURN
END
During execution of store procedure by EntityFramework (in order to get metadata), condition 0=1 is removed and empty table of Table type is returned which is enough to get metadata. This code is never trigerred in production because of impossible condition.

How to alter table from C# code

I want to add one column to an existing table in C#, if it doesn't already exist. I know I have to use an 'alter table' command.
But I am not able to fire that command in my C# code.
How can I do?
I'm using Visual Studio 2010 and Sql Server 2008.
this :
Use [DatabaseName]
Go
if Not exists( Select * from sys.columns As clm
where clm.object_id = OBJECT_ID(N'[TableName]')
And clm.name = N'[ColumnName]'
)
Begin
Alter Table TableName
Add ColumnName DataTypeName
End

How to get SCHEMA.TABLES from another database?

I want to get a list of tables exist in another database. for example if i have connected DB1 database and i want to get a list of tables from DB2 then how is it possible?
I know there are some another approaches like connect DB2 and execute insert query to insert schema into #temp table then connect DB1 using USE [DB1] statement and use that #temp table.
But, I don't want to change my sql connection at runtime. Because, there are some dependencies i have created on my sql connection.
UPDATED:
Database can be restored in same server. Now i am using following query to get Table List from the database.
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'
version of sql server is 2005.
You need sp_addlinkedserver()
http://msdn.microsoft.com/en-us/library/ms190479.aspx
Example:
exec sp_addlinkedserver #server = 'test'
then
select * from [server].[database].[schema].[table]
Copy Table Schema and Data From One Database to Another Database in SQL Server
Remus Rusanu has already mentioned Here
Try this Query
declare #sql nvarchar(max);
set #sql = N'select cast(''master'' as sysname) as db_name, name collate Latin1_General_CI_AI, object_id, schema_id, cast(1 as int) as database_id from master.sys.tables ';
select #sql = #sql + N' union all select ' + quotename(name,'''')+ ', name collate Latin1_General_CI_AI, object_id, schema_id, ' + cast(database_id as nvarchar(10)) + N' from ' + quotename(name) + N'.sys.tables'
from sys.databases where database_id > 1
and state = 0
and user_access = 0;
exec sp_executesql #sql;
I assume that you've set the database in your connection string.
If the other database is on the same server, you can reference them with a three-part name
select * from otherdb.schema.table
If it's not, you can use a four-part name
select * from otherserver.otherdb.schema.table
But my advice would be to not do that directly but rather create a synonym for each object that you plan to reference externally.
That way you gain some flexibility if you do something like rename the external database.

Categories

Resources