Возникли проблемы с правильным использованием @declare в итерации - PullRequest
0 голосов
/ 06 июня 2019

Я разместил еще один вопрос здесь

Но оказывается, что первое исправление моей проблемы было более простым, и я установил счетчик для повторения в неправильном месте. Реальная проблема заключается в том, что значение, которое я создаю в имени БД, неверно, когда я пытаюсь выполнить итерацию.

Вот код, который был исправлен дружественным человеком в Переполнении стека, и они уведомили меня, чтобы я отправил второй вопрос, чтобы прояснить эту проблему с неправильным форматом объявления.

Основная проблема связана с этой строкой [EDDS '+ cast (@databasename как nvarchar (128)) +']. [EDDSDBO]. [Document]

--check if the #databases table is already present and then drop it
IF OBJECT_ID('tempdb..#databases', 'U') IS NOT NULL
    drop table #databases;


--create the temp table as outside the loop
create table #databases(
    ID INT IDENTITY,
    ArtifactID VARCHAR(20) -- not sure of this ID's data type
)


--check if your temp table exists and drop if necessary
IF OBJECT_ID('tempdb..#temptable', 'U') IS NOT NULL
    drop table #temptable;

--create the temp table as outside the loop
create table #temptable(
    fileSize dec,
    extractedTextSize dec
)

--this will allow the population of each database name
DECLARE @databasename sysname = '' 

-- initialze to 1 so it matches first record in temp table
DECLARE @LoopOn int = 1; 

--this will be the max  count from table
DECLARE @MaxCount int = 0; 

--Once this first statement has been run there will now be a number column 
that is associated with the artificatID. Each database has an area that is 
--      titled [EDDS'artifactID']. So if the artifactID = 1111111 then the 
DB would be accessed at [EDDS1111111]

-- do insert here so it adds the ID column
INSERT INTO #databases(
    ArtifactID
)
SELECT ArtifactID 
FROM edds.eddsdbo.[Case]
where name like '%Review%'

-- sets the max number of loops we are going to do
select @MaxCount = COUNT(*) 
FROM #databases;

while @LoopOn <= @MaxCount
    BEGIN
        -- your table has IDENTITY so select the one for the loop your on 
(initalize to 1)
        select @databasename = ArtifactID 
        FROM #databases
        WHERE ID = @LoopOn;

        --generate your sql using the @databasename variable, if you want 
to make 
        --the database and table names dynamic too then you can use the 
same formula

        insert into #temptable
        select SUM(fileSize)/1024/1024/1024, 
SUM(extractedTextSize)/1024/1024
        -- dont know/think this will work like this?  If not you have to 
use dynamic SQL 
        FROM [EDDS'+cast(@databasename as nvarchar(128))+'].[EDDSDBO]. 
[Document] ed
    where ed.CreatedDate >= (select CONVERT(varchar,dateadd(d,- (day(getdate())),getdate()),106))

    -- remove all deletes/etc and just add one to the @LoopOn and it will be selected above based off the ID
    select @LoopOn += 1
end

-- Query the final values in the temp table after the iteration is complete
select filesize+extractedTextSize as Gigs 
FROM #temptable

Я получаю сообщение об ошибке

Неверное имя объекта 'EDDS' + cast (@databasename как nvarchar (128)) + '. EDDSDBO.Document'

Если я вручную введу EDDS1111111.EDDSDBO.Document, все будет нормально.

Если я установлю declare @databasename nvarchar(128) = 1111111, это также нарушит код. Я думаю, что ошибка связана с тем, как я ее приводил или добавляю в утверждение.

Спасибо за любую помощь, которую вы можете предложить

1 Ответ

1 голос
/ 06 июня 2019

Вы не можете построить SQL и выполнить, как вы выше, или вы получаете сообщение об ошибке, вам нужно сделать весь оператор динамическим.Ниже приведены два различных решения для этого в зависимости от размера / сложности (в случае, если вы не опубликовали полный код).

Это решит вашу проблему и создаст динамический SQL для каждой строки в вашей таблице.

Я обновил это, чтобы иметь 2 решения: одно, если у вас не так много строк для выполнения динамического SQL, и другое, если у вас есть комплекс или много для выполнения.

Второе решение может быть урезано с использованием меньшего количества кода для вашего конкретного сценария, но если вы сделаете это так, как описано ниже, вы сможете использовать его для гораздо большего количества сценариев, просто изменив таблицы SQL и temp, которые вы вставляете вдля ваших результатов.

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

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

-- this is used to add line breaks to make code easier to read
DECLARE @NewLine AS NVARCHAR(MAX) = CHAR(10)

-- to hold your dynamic SQL for all rows/inserts at once
DECLARE @sql NVARCHAR(MAX) = N'';

-- create temp table to insert your dynamic SQL results into 
IF OBJECT_ID('tempdb..#DatabaseSizes', 'U') IS NOT NULL
    DROP TABLE #DatabaseSizes;

create table #DatabaseSizes(
    fileSize DECIMAL,
    extractedTextSize DECIMAL
)

SELECT @sql = @sql + N'' + 
    'select SUM(fileSize)/1024/1024/1024 as fileSize, SUM(extractedTextSize)/1024/1024 as extractedTextSize ' + @NewLine +          
    'FROM [EDDS' + CAST(ArtifactID as nvarchar(128)) + '].[EDDSDBO].[Document] ed ' + @NewLine +
    'where ed.CreatedDate >= (select CONVERT(varchar,dateadd(d,- (day(getdate())),getdate()),106)) ; ' + @NewLine + @NewLine
FROM edds.eddsdbo.[Case]
WHERE name like '%Review%'


-- for testing/validating 
PRINT @sql

INSERT INTO #DatabaseSizes (
    fileSize,
    extractedTextSize
)
-- executes all the dynamic SQL we just generated
EXEC sys.sp_executesql @SQL;


SELECT * 
FROM #DatabaseSizes


-- &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
-- for solution with a LOT of records from your table that is too large or complex to execute all at once using the above 
-- this will generate seperate dynamic SQL for each row in your table
-- &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&


DECLARE @NewLine AS NVARCHAR(MAX) = CHAR(10)
DECLARE @IdOnFromLoop INT
DECLARE @DynamicSQLFromLoop NVARCHAR(MAX)

-- table to insert data into that our dynamic SQL creates
IF OBJECT_ID('tempdb..#DatabaseSizes', 'U') IS NOT NULL
    DROP TABLE #DatabaseSizes;

create table #DatabaseSizes(
    fileSize DECIMAL,
    extractedTextSize DECIMAL
)

-- this is to hold each rows dynamic SQL so we can loop through them and execute each statement one at a time
IF OBJECT_ID('tempdb..#DynamicSQLPerLoop', 'U') IS NOT NULL
    DROP TABLE #DynamicSQLPerLoop;

create table #DynamicSQLPerLoop(
    ID INT IDENTITY,
    DynamicSQL NVARCHAR(MAX)
)

-- here we build our our dynamic SQL we want for each row in the table to be executed
INSERT INTO #DynamicSQLPerLoop (
    DynamicSQL
)
SELECT 'select SUM(fileSize)/1024/1024/1024 as fileSize, SUM(extractedTextSize)/1024/1024 as extractedTextSize ' + @NewLine +           
    'FROM [EDDS' + CAST(ArtifactID as nvarchar(128)) + '].[EDDSDBO].[Document] ed ' + @NewLine +
    'where ed.CreatedDate >= (select CONVERT(varchar,dateadd(d,- (day(getdate())),getdate()),106)) ; ' + @NewLine + @NewLine
FROM edds.eddsdbo.[Case]
WHERE name like '%Review%'



-- for testing/validating all the rows
SELECT * FROM #DynamicSQLPerLoop

-- need to initalize ID to start on, could default to 1, but if no recordsd found would try to do loop and error out
SELECT @IdOnFromLoop = MIN(ID) 
FROM #DynamicSQLPerLoop

-- now we just loop through all the records, until no more are found
WHILE @IdOnFromLoop IS NOT NULL
    BEGIN
        -- need to get dynamic SQL statement to execute for the loop we are on now
        SELECT @DynamicSQLFromLoop = DynamicSQL
        FROM #DynamicSQLPerLoop
        WHERE ID = @IdOnFromLoop

        -- now we insert the data into our table by executing the dynamic SQL 
        INSERT INTO #DatabaseSizes (
            fileSize,
            extractedTextSize
        )       
        EXEC sys.sp_executesql @DynamicSQLFromLoop


        -- now we get the ID that is one higher than the one we just did, and if none found will exit loop
        SELECT @IdOnFromLoop = MIN(ID) 
        FROM #DynamicSQLPerLoop
        WHERE ID > @IdOnFromLoop

    END -- end looping


SELECT * 
FROM #DatabaseSizes
...