Динамический T-SQL - добавление параметризованных подзапросов во временную таблицу агрегирования - PullRequest
2 голосов
/ 10 мая 2019

Схема этой базы данных нечетная;Все данные разбиты по типам и хранятся в соответствующих таблицах.Когда требуется доступ к «таблице», она должна быть сшита вместе, поля по идентификатору таблицы, значения по идентификатору поля.Он медленный, и в основном о нем позаботились сторонние сценарии спагетти.

У меня есть несколько таблиц, отформатированных примерно так:

dbo.TagSource:

TagSourceId | Table Name | ...
------------------------------------
01            Table 1
02            Table 2

dbo.Tag:

TagId | TagSourceId | Field Name | RawTableName | ...
------------------------------------------------------
11      01            Name         dbo.StringValue
12      01            Age          dbo.IntegerValue
13      01            Balance      dbo.DecimalValue
14      01            Document     dbo.FileValue
15      02            Name         ExternalTable1
16      02            Height       ExternalTable1
17      02            Occupation   ExternalTable2

dbo. [String / Integer / Decimal / File / ...] Таблицы значений:

ValueId | TagId | Value
-------------------------------
101       11      John Smith
102       11      Jane Smith
103       11      John Doe 
104       11      Jane Doe
105       11      Thanos

ЦЕЛЬ: Чтобы создатьСценарий запроса TSQL (хранимая процедура / функция / представление), которому задан TagSourceID в качестве параметра / объявленной переменной, возвращает соответствующую таблицу.В конце концов, это будет использоваться для создания представлений, пока у меня не будет полосы пропускания для замены и замены схемы БД.

Каков оптимальный способ сделать это? Это сложно, так как числополя в каждой таблице являются динамическими, а определения столбцов неизвестны.

Попытки: Я пытался использовать временные таблицы для хранения своих сводных данных и данных, которые нужно добавить, но вскоре обнаружил, чтопоскольку определения столбцов должны быть предопределены, мне пришлось прибегнуть к неловкому перемещению данных между временными таблицами.Кажется неоптимальным, даже если это работает, но это не так.

Логика:

BEGIN LOOP
#B = #A Join (Select value From dbo...Value Table...)
Drop Table #A
#A = #B
Drop Table #B
END

Сценарий: возвращает ошибку There is already an object named '#ResultSet' in the database., хотя я явно DROP TABLE #ResultSetстрока перед строкой ошибки.

--*** DECLARATION ***
USE [DB_Arc];
GO

-- ***************
-- Target Table Id
DECLARE @TagSourceID INT = 130;
-- ***************

-- Temporary Tables - DROP IF EXIST
-- #TempA
IF OBJECT_ID(N'#ResultSet') IS NOT NULL DROP TABLE #ResultSet;
CREATE TABLE dbo.#ResultSet (
    DateTime DATETIME,
    GroupID NVARCHAR(50)
);
-- #TempB
IF OBJECT_ID(N'#JoinSet') IS NOT NULL DROP TABLE #JoinSet;
CREATE TABLE dbo.#JoinSet (
    DateTime DATETIME,
    GroupID NVARCHAR(50)
);

-- Target Fields - Iterator
DECLARE field_cursor CURSOR FOR
SELECT Id, Name, RawTableName
FROM dbo.[Tag]
WHERE TagSourceId = @TagSourceID
AND Name NOT IN ('DateTime','GroupId')
AND TagTypeId NOT IN (6,7)
ORDER BY Id;

-- Local Variables
DECLARE @TagId bigint, @TagName nvarchar(250), @RawTableName nvarchar(250)

OPEN field_cursor;

-- Initial first fetch and store
FETCH NEXT FROM field_cursor
INTO @Tag, @TagName, @RawTableName;

-- Verify that the table where the values are stored is not external.
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @RawTableName)
BEGIN --IF BLOCK
    -- Check @@FETCH_STATUS for any other rows to fetch
    WHILE @@FETCH_STATUS = 0
    -- Loop through each field/column...
    BEGIN --LOOP START
        -- full outer join values from rawtables to ResultSet

        -- #TempB = #TempA Join (Select value From dbo...Value Table...)
        DECLARE @field nvarchar(500);
        SET @field = 
        N'SELECT a.*, b.value AS ' + QuoteName(@TagName) +
        N'INTO #JoinSet
          FROM #ResultSet a
          FULL OUTER JOIN ( 
            SELECT
                CONVERT(datetime2, [DateTime], 1) as DateTime,
                [GroupId],
                [Value]
            FROM dbo.' + QuoteName(@RawTableName) +
            N'WHERE TagId = ' + QuoteName(@TagId) + N'AND 
            IsDeleted=0 ) b
          ON a.DateTime = b.DateTime AND a.GroupId = b.GroupId
        ';
        EXEC sp_executesql @field;

        -- DROP TABLE #TempA
        IF OBJECT_ID(N'#ResultSet') IS NOT NULL DROP TABLE #ResultSet
        -- #TempA = #TempB
        -- ***** ERROR BLOCK START *****
        -- Returns 'There is already an object named '#ResultSet' in the database.'
        SELECT *
        INTO #ResultSet
        FROM #JoinSet;
        -- ***** ERROR BLOCK END *****
        -- DROP TABLE #TempB
        DROP TABLE #JoinSet;


        -- Iterate values to next field
        FETCH NEXT FROM field_cursor
        INTO @Tag, @TagName, @RawTableName;
    END --LOOP END

END --IF BLOCK END

CLOSE field_cursor;
DEALLOCATE field_cursor;

SELECT * FROM #ResultSet;
DROP TABLE #ResultSet;

GO

Я также пытался создать рекурсивную процедуру / функцию, чтобы справиться с этим, но у меня возникают проблемы с использованием курсора в качестве рекурсивного условия и таблицы переменных / tempтаблицы со статическими определениями столбцов по-прежнему остаются проблемой.

Любая помощь будет принята с благодарностью.Если у вас есть вопросы о назначении этого сценария или какие-либо общие предложения, я бы очень хотел его услышать.

1 Ответ

1 голос
/ 10 мая 2019

Три проблемы в скрипте -

  1. Должна быть синхронизация между именем столбца и номером столбца между # временной таблицей и строкой, возвращаемой из динамического сценария из «SELECT a. *,b.value AS .......... ".
  2. Нет необходимости создавать вторую таблицу #Teorary (#JoinSet), поскольку мы можем непосредственно вставить ее в таблицу #ResultSet.
  3. Сценарий «SELECT * INTO #Temp ......» всегда пытается создать временную таблицу, в которой таблица уже создана.Вместо этого нам нужно изменить скрипт на что-то вроде «INSERT INTO #ResultSet SELECT a. *, B.value .........».
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...