Попытка сохранить типы данных при динамическом дублировании таблицы

Я пытаюсь дублировать таблицу, динамически сохраняя типы данных исходных таблиц.

Следующий запрос работает, за исключением числового типа данных. Столбцы числовых типов данных имеют вид nvarchar.

Что нужно изменить, чтобы сохранить числовые типы данных?

DECLARE @TableName = 'Table'
DECLARE @SchemaName = 'Schema'

DECLARE @SQL NVARCHAR(MAX) = 'CREATE TABLE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME('TablePrefix_' + SUBSTRING(@TableName, 3, LEN(@TableName)-2)) + ' (
    [AuditId] INT IDENTITY(1,1) NOT NULL,
    [AuditAction] VARCHAR(50) NOT NULL,
    [AuditDateTime] DATETIME NOT NULL,    
    ' + 
    STUFF((
        SELECT ',' + '[' + c.name + '] ' + 
                CASE 
                    WHEN c.system_type_id IN (167, 175, 231, 239) AND c.max_length = -1 THEN 'VARCHAR(MAX)'
                    WHEN c.system_type_id IN (167, 175, 231, 239) THEN 'VARCHAR(' + CAST(CASE WHEN c.max_length < 1 THEN 1 ELSE c.max_length END AS VARCHAR(5)) + ')'
                    WHEN c.system_type_id IN (106, 108, 165, 173, 231, 239) AND c.max_length = -1 THEN 'NVARCHAR(MAX)'
                    WHEN c.system_type_id IN (106, 108, 165, 173, 231, 239) THEN 'NVARCHAR(' + CAST(CASE WHEN c.max_length < 1 THEN 1 ELSE c.max_length / 2 END AS VARCHAR(5)) + ')'
                    WHEN c.system_type_id IN (40) THEN 'CHAR(' + CAST(CASE WHEN c.max_length < 1 THEN 1 ELSE c.max_length END AS VARCHAR(5)) + ')'
                    WHEN c.system_type_id IN (41) THEN 'NCHAR(' + CAST(CASE WHEN c.max_length < 1 THEN 1 ELSE c.max_length END AS VARCHAR(5)) + ')'
                    WHEN c.system_type_id IN (48, 52, 56) THEN 'INT'
                    WHEN c.system_type_id IN (127) THEN 'BIGINT'
                    WHEN c.system_type_id IN (59, 60, 62) THEN 'SMALLINT'
                    WHEN c.system_type_id = 104 THEN 'BIT' -- Changed from 'TINYINT' to 'BIT'
                    WHEN c.system_type_id IN (106, 108, 122, 127, 130, 131, 143, 167, 173, 175, 189, 231, 239) THEN TYPE_NAME(c.user_type_id)
                    ELSE TYPE_NAME(c.system_type_id)
                END +
                CASE WHEN c.is_nullable = 1 THEN ' NULL' ELSE ' NOT NULL' END
        FROM sys.columns c
        WHERE c.object_id = OBJECT_ID(@SchemaName + '.' + @TableName)
        FOR XML PATH(''), TYPE
    ).value('.[1]','nvarchar(max)'), 1, 1, '')
    + ')'
EXEC(@SQL)

Числовое значение — 108, поэтому создайте для него отдельный регистр.

Bart McEndree 17.06.2024 19:44

Если вы просто хотите повторить таблицу, почему бы и нет SELECT TOP (0) * INTO dbo.AnotherTable FROM dbo.YourTable;? Если вам нужно добавить дополнительные столбцы, вы можете явно указать их в файле SELECT.

Thom A 17.06.2024 19:48

Спасибо всем за вашу помощь! Я попробовал добавить 108, «WHEN c.system_type_id IN (108) THEN NUMERIC», но это не сработало, все равно получается nvarchar. Я подумаю о том, чтобы попасть в Select top 0, это хорошая идея.

JM1 17.06.2024 19:49

Тип данных для рассматриваемого столбца — числовой (18,2).

JM1 17.06.2024 20:02

у вас есть случай, когда уже используются 106/108 (десятичные/числовые), удалите их и добавьте что-то вроде: WHEN c.system_type_id IN (106) then CONCAT('DECIMAL(', [precision], ',', scale, ')') WHEN c.system_type_id IN (108) then CONCAT('NUMERIC(', [precision], ',', scale, ')')

siggemannen 17.06.2024 20:03

Лично мне очень нравится использовать: select * from sys.dm_exec_describe_first_result_set('select * from sometable', null, 0) для получения материалов столбца

siggemannen 17.06.2024 20:04

@siggemannen, вы предложили, КОГДА сработало! Спасибо за помощь и за идею, где взять материал для колонки. Если вы опубликуете это как ответ, я отмечу это для вас.

JM1 17.06.2024 20:12
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
7
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я немного переписал ваш скрипт с исправленными типами данных:

DECLARE @TableName sysname = 't_test'
DECLARE @SchemaName sysname = 'dbo'

CREATE TABLE t_test (i int, n decimal(19,2), z varchar(3), q nvarchar(300), n1 numeric(19,3))

DECLARE @SQL NVARCHAR(MAX) = 'CREATE TABLE ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME('TablePrefix_' + SUBSTRING(@TableName, 3, LEN(@TableName)-2)) + ' (
    [AuditId] INT IDENTITY(1,1) NOT NULL,
    [AuditAction] VARCHAR(50) NOT NULL,
    [AuditDateTime] DATETIME NOT NULL,    
    ' + 
    STUFF((
        SELECT  ',' + '[' + c.name + '] ' + 
                CASE 
                    WHEN c.system_type_id IN (167, 175, 231, 239) AND c.max_length = -1 THEN 'VARCHAR(MAX)'
                    WHEN c.system_type_id IN (167, 175, 231, 239) THEN 'VARCHAR(' + CAST(CASE WHEN c.max_length < 1 THEN 1 ELSE c.max_length END AS VARCHAR(5)) + ')'
                    WHEN c.system_type_id IN (165, 173, 231, 239) AND c.max_length = -1 THEN 'NVARCHAR(MAX)'
                    WHEN c.system_type_id IN (165, 173, 231, 239) THEN 'NVARCHAR(' + CAST(CASE WHEN c.max_length < 1 THEN 1 ELSE c.max_length / 2 END AS VARCHAR(5)) + ')'
                    WHEN c.system_type_id IN (40) THEN 'CHAR(' + CAST(CASE WHEN c.max_length < 1 THEN 1 ELSE c.max_length END AS VARCHAR(5)) + ')'
                    WHEN c.system_type_id IN (41) THEN 'NCHAR(' + CAST(CASE WHEN c.max_length < 1 THEN 1 ELSE c.max_length END AS VARCHAR(5)) + ')'
                    WHEN c.system_type_id IN (48, 52, 56) THEN 'INT'
                    WHEN c.system_type_id IN (127) THEN 'BIGINT'
                    WHEN c.system_type_id IN (59, 60, 62) THEN 'SMALLINT'
                    WHEN c.system_type_id IN (106) THEN CONCAT('DECIMAL(', [precision], ',', scale, ')')
                    WHEN c.system_type_id IN (108) THEN CONCAT('NUMERIC(', [precision], ',', scale, ')')
                    WHEN c.system_type_id = 104 THEN 'BIT' -- Changed from 'TINYINT' to 'BIT'
                    ELSE TYPE_NAME(c.system_type_id)
                END +
                CASE WHEN c.is_nullable = 1 THEN ' NULL' ELSE ' NOT NULL' END
        FROM    sys.columns c
        WHERE   c.object_id = OBJECT_ID(@SchemaName + '.' + @TableName)
        ORDER BY c.column_id
        FOR XML PATH(''), TYPE
    ).value('.[1]','nvarchar(max)'), 1, 1, '')
    + ')'

Но подобные сценарии обычно упускают некоторые детали и странные типы данных, поэтому в последнее время я использую dm_exec_describe_first_result_set, который легко возвращает вам метаданные:

SELECT  *
FROM    sys.dm_exec_describe_first_result_set('select * from t_test', NULL, 0)

Другая проблема вашего сценария заключается в том, что он не обрабатывает user_type_ids, который является предпочтительным способом создания метаданных. Например: sysname — это определяемый пользователем тип для nvarchar, но ваш скрипт обрабатывает его неправильно, но dm_exec_describe_first_result_set возвращает их оба.

Также следите за тем, чтобы включать ORDER BY при создании столбцов, потому что в противном случае вы не гарантированно получите правильную последовательность столбцов.

Спасибо за помощь, я благодарен!

JM1 18.06.2024 12:25

Другие вопросы по теме

Sp_executesql в цикле, выполнение которого занимает ВЕЧНОСТЬ. Замедление во время бега. Утечка памяти?
Как с помощью T-SQL получить последнюю среднюю чистую цену за последний проданный год?
Оптимизация производительности SQL Server: объединение таблиц бюджета и фактических данных после одинаковых преобразований CTE и объединений измерений
Как преобразовать «Типы» свойства класса List<string> foo в таблицу SQL Server [Types](FooID int, name nvarchar) с помощью Entity Framework Core
Я постоянно получаю сообщение «Неверный синтаксис рядом с ключевым словом «КОГДА».» Ошибка в моем SQL-запросе
Создайте столбец rowID/identity в подзапросе перекрестного соединения
Использована статистика из кэша планов XML для запроса с заданным QueryPlanHash
Два перекрестных соединения дают повторяющийся результат с двумя отдельными значениями
Условно-рекурсивный запрос в SQL Server
Каков максимальный поддерживаемый уровень совместимости для EF6?