Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

procedure to drop system-versioned temporal tables

I'm looking for a procedure to drop system-versioned temporal tables, ideally without using dynamic SQL. I've looked through the Microsoft documentation and figured out how to get the autogenerated history table name, but I only know a little about cursors and even less about dynamic SQL.

You can't just drop a temporal table. You must first disable versioning, which will cause the history table to become an ordinary table. Then you can drop both the temporal table and its corresponding history table.

ALTER TABLE [dbo].[TemporalTest] SET ( SYSTEM_VERSIONING = OFF )
GO
DROP TABLE [dbo].[TemporalTest]
GO
DROP TABLE [dbo].[TemporalTestHistory]
GO

I am using temporal tables with autogenerated history tables, so I don't know their names. However, the Microsoft docs provide information about how to list the history tables, so I have a way of getting those names.

select schema_name(t.schema_id) as temporal_table_schema,
     t.name as temporal_table_name,
    schema_name(h.schema_id) as history_table_schema,
     h.name as history_table_name,
    case when t.history_retention_period = -1 
        then 'INFINITE' 
        else cast(t.history_retention_period as varchar) + ' ' + 
            t.history_retention_period_unit_desc + 'S'
    end as retention_period
from sys.tables t
    left outer join sys.tables h
        on t.history_table_id = h.object_id
where t.temporal_type = 2
order by temporal_table_schema, temporal_table_name

I was hoping I could use a subquery with the DROP statement, e.g. DROP TABLE (SELECT '#t'). This throws a syntax error.

I'm looking for a stored procedure which takes two parameters: the name of the table to be dropped and whether the drop should take place if there's any data in the table (eg, must ROWCOUNT=0). Can anyone help, or recommend cursors over Dynamic SQL, or recommend another technique? Thanks!

like image 985
Frank Avatar asked Apr 04 '19 17:04

Frank


People also ask

How do I delete data from a system-versioned table in SQL?

To delete data from a system-period temporal table, use the DELETE FROM statement. For example, the owner of policy B345 decides to cancel insurance coverage. The data was deleted on September 1, 2011 (2011-09-01) from the table that was updated in the Updating data in a system-period temporal table topic.

What is a system-versioned temporal table?

Temporal tables (also known as system-versioned temporal tables) are a database feature that brings built-in support for providing information about data stored in the table at any point in time, rather than only the data that is correct at the current moment in time.

How do I change my temporal table?

There are two ways to alter temporal tables: Via Transact-SQL script. For more details see https://msdn.microsoft.com/en-us/library/mt591016.aspx. Using SQL Server Data Tools, that integrates with Visual Studio.


2 Answers

You can't issue DDL against data (like the output of a subquery), or pass an entity name as a variable to a statement; you need dynamic SQL.

CREATE PROCEDURE dbo.DropTemporalTable
  @schema sysname = N'dbo',
  @table  sysname
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @sql nvarchar(max) = N'';

  SELECT @sql += N'ALTER TABLE ' + src + N' SET (SYSTEM_VERSIONING = OFF);
    DROP TABLE ' + src  + N';
    DROP TABLE ' + hist + N';'
  FROM
  (
    SELECT src = QUOTENAME(SCHEMA_NAME(t.schema_id))
               + N'.' + QUOTENAME(t.name),
          hist = QUOTENAME(SCHEMA_NAME(h.schema_id))
               + N'.' + QUOTENAME(h.name)
    FROM sys.tables AS t
    INNER JOIN sys.tables AS h
    ON t.history_table_id = h.[object_id]
    WHERE t.temporal_type = 2
      AND t.[schema_id] = SCHEMA_ID(@schema)
      AND t.name = @table
  ) AS x;

  EXEC sys.sp_executesql @sql;
END
GO
like image 153
Aaron Bertrand Avatar answered Nov 05 '22 23:11

Aaron Bertrand


I am using temporal tables with autogenerated history tables, so I don't know their names. However, the Microsoft docs provide information about how to list the history tables, so I have a way of getting those names.

I don't know if you know this but you can specify the name of the history table in several ways.

If you create your own history table:

ALTER TABLE [TemporalTable] SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[HistoryTable]))

Or if you want your history table created for you:

CREATE TABLE [dbo].[TemporalTable](
    [Id] UNIQUEIDENTIFIER NOT NULL CONSTRAINT [DF_TemporalTable_Id] DEFAULT(NEWID()),
    [SysStartTime] DATETIME2(2) GENERATED ALWAYS AS ROW START NOT NULL CONSTRAINT [DF_TemporalTable_SysStartTime] DEFAULT(SYSUTCDATETIME()),
    [SysEndTime] DATETIME2(2) GENERATED ALWAYS AS ROW END NOT NULL CONSTRAINT [DF_TemporalTable_SysEndTime] DEFAULT(CONVERT(DATETIME2(2),'9999-12-31 23:59:59.99')),
 CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 100) ON [PRIMARY],
    PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime])
) ON [PRIMARY]
WITH
(
SYSTEM_VERSIONING = ON ( HISTORY_TABLE = [dbo].[HistoryTable] )
)

Other than that I don't know of a way to create a stored procedure which takes a table name as a parameter and drops it without using dynamic SQL. Something like below will be necessary. Depending on if you want the stored procedure to look up the name of the history table linked to it you can add the query you mentioned in your post instead of a parameter.

CREATE PROCEDURE DeleteTemporalTable (@tableName NVARCHAR(MAX), @historyTableName) AS
BEGIN
    EXEC ('
    IF EXISTS (
        SELECT
            1
        FROM
            SYSOBJECTS
        WHERE
            id = OBJECT_ID(N''[dbo].[' + @tableName + ']'') AND
            OBJECTPROPERTY(id, N''IsTable'') = 1 AND
            OBJECTPROPERTY(id, N''TableTemporalType'') = 2
    )
    BEGIN
        ALTER TABLE [dbo].[' + @tableName + '] SET (SYSTEM_VERSIONING = OFF)
    END
    DROP TABLE IF EXISTS [dbo].[' + @tableName + ']
    DROP TABLE IF EXISTS [dbo].[' + @historyTableName + ']')
END

If you're just looking for a way to delete all tables, and you want something that works with temporal tables, this is a script I use:

DECLARE @temporalTableName varchar(max) = (SELECT TOP 1 [name] FROM sys.tables WHERE [temporal_type_desc] = 'SYSTEM_VERSIONED_TEMPORAL_TABLE')
WHILE @temporalTableName IS NOT NULL
BEGIN
    EXEC('ALTER TABLE [dbo].[' + @temporalTableName + '] SET (SYSTEM_VERSIONING = OFF)')
    SET @temporalTableName = (SELECT TOP 1 [name] FROM sys.tables WHERE [temporal_type_desc] = 'SYSTEM_VERSIONED_TEMPORAL_TABLE')
END
DECLARE @tableName varchar(max) = (SELECT TOP 1 [name] FROM sys.tables)
WHILE @tableName IS NOT NULL
BEGIN
    EXEC('DROP TABLE [dbo].[' + @tableName + ']')
    SET @tableName = (SELECT TOP 1 [name] FROM sys.tables)
END
like image 30
bdebaere Avatar answered Nov 05 '22 23:11

bdebaere