Запустите этот скрипт на всех базах данных

У меня есть приведенный ниже скрипт, который отлично работает и дает все нужные мне результаты. Однако он делает это только для текущего контекста БД. У меня несколько сотен баз данных в моем экземпляре, и мне нужно получить результаты для всех баз данных за одно выполнение. Как изменить скрипт, чтобы он работал для всех баз данных.

IF OBJECT_ID('tempdb..#AllInfo', N'U') IS NOT NULL DROP TABLE #AllInfo;

CREATE TABLE #AllInfo (
dbase_name nvarchar(200),
tbl_name nvarchar(300),
column_name nvarchar(300),
min_date datetime
)
DECLARE @Statement varchar(2000);
DECLARE cr_MaxDateTime CURSOR LOCAL FOR
    SELECT 'SELECT DB_NAME(), ' + '''' + TAB.name + ''''  + ', ' + '''' + COL.name + '''' +' ,MIN([' + COL.name +']) AS ''Min' + COL.name + '_' + TAB.name + ''' FROM [' + SCH.name + '].[' + TAB.name + ']'
    FROM   sys.schemas AS SCH
    INNER  JOIN sys.tables AS TAB
           ON TAB.schema_id = SCH.schema_id
    INNER  JOIN sys.columns AS COL
           ON COL.object_id = TAB.object_id
    INNER  JOIN sys.types AS UDT
           ON COL.user_type_id = UDT.user_type_id
    INNER  JOIN sys.types AS TYP
           ON TYP.system_type_id = UDT.system_type_id AND
              TYP.user_type_id = TYP.system_type_id   AND
              TYP.name IN ('date', 'datetime2', 'datetimeoffset', 'datetime', 'time')
    ORDER  BY SCH.name, TAB.name, COL.name
FOR READ ONLY;
OPEN cr_MaxDateTime;
FETCH cr_MaxDateTime INTO @Statement;
WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT @Statement;
    INSERT INTO #AllInfo
    EXECUTE(@Statement);
    FETCH cr_MaxDateTime INTO @Statement;
END;
CLOSE cr_MaxDateTime;
DEALLOCATE cr_MaxDateTime;

select * from #AllInfo

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

Отвечает ли это на ваш вопрос? SQL Server: запустить скрипт на всех базах данных

Larnu 20.02.2023 11:15

Отвечает ли это на ваш вопрос? Выполнение SQL-запроса к нескольким базам данных

Larnu 20.02.2023 11:16

Re: sp_msforeachdb, Это ничем не отличается от длинного скрипта, просто создайте большую длинную строку со всем этим и прикрепите ?. к каждой таблице, чтобы он поправил базу данных или добавил ЕГЭ?. Однако это довольно хлопотно, так как вам обычно приходится заменять множество кавычек и т. д., Чтобы заставить его работать.

siggemannen 20.02.2023 11:23

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

Vineeth Nair 20.02.2023 12:00
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
4
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Предполагая, что вы используете современную и поддерживаемую версию SQL Server, более простой способ удалить таблицу #temp (конечно, ненужную, если вы поместите этот код в хранимую процедуру):

DROP TABLE IF EXISTS #AllInfo;

CREATE TABLE #AllInfo 
(
  dbase_name  nvarchar(130),
  tbl_name    nvarchar(260),
  column_name nvarchar(130),
  min_date    datetime
);

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

DECLARE @base   nvarchar(max) = N'SELECT DB_NAME(),
                                  CONCAT($schn$, char(46), $tbn$), 
                                  $coln$, MIN($col$) FROM $sch$.$tb$;',
    @nested nvarchar(max) = N'DECLARE @sql nvarchar(max) = SPACE(0);
SELECT @sql += REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@base, 
  N''$sch$'', QUOTENAME(s.name)), N''$schn$'', QUOTENAME(s.name, char(39))), 
  N''$tb$'',  QUOTENAME(t.name)), N''$tbn$'',  QUOTENAME(t.name, char(39))), 
  N''$col$'', QUOTENAME(c.name)), N''$coln$'', QUOTENAME(c.name, char(39)))
FROM sys.schemas AS s
INNER JOIN sys.tables AS t  ON s.[schema_id] = t.[schema_id]
INNER JOIN sys.columns AS c ON t.[object_id] = c.[object_id]
WHERE EXISTS (
  SELECT 1 FROM sys.types 
  WHERE system_type_id IN (40,41,42,43,61) 
  AND user_type_id = c.user_type_id);
INSERT #AllInfo EXEC sys.sp_executesql @sql;';

DECLARE @context nvarchar(2000), @c cursor;

SET @c = CURSOR FORWARD_ONLY STATIC READ_ONLY FOR 
         SELECT QUOTENAME(name) + N'.sys.sp_executesql'
         FROM sys.databases WHERE state = 0 AND database_id > 4;

OPEN @c; FETCH NEXT FROM @c INTO @context;

WHILE @@FETCH_STATUS <> -1
BEGIN
  EXEC @context @nested, N'@base nvarchar(max)', @base;
  FETCH NEXT FROM @c INTO @context;
END

По сути, нам нужно подготовить оператор, который можно запустить в каждой базе данных, а затем внутри каждой базы данных для каждого столбца, соответствующего нашим критериям. Таким образом, у нас есть вложенный динамический SQL, который настраивается внутри курсора базы данных, заменяя токены, такие как $sch$, на фактическое имя схемы, $tb$ на фактическое имя таблицы и $col$ на фактическое имя столбца. REPLACE() запутан, и я изначально пошел по этому пути, потому что я ссылался на каждый токен более одного раза, но это усложнилось, потому что в некоторых местах его нужно было отображать как строковый литерал, а не как имя объекта в квадратных скобках.

Окончательно:

SELECT * FROM #AllInfo;

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

Здравствуйте @Aaron Bertrand, большое спасибо, и я очень ценю время, которое вы потратили на написание кода и его подробное объяснение вместе со ссылками. Я все еще пытаюсь осмыслить это и лучше понять [частично из-за собственной неопытности :)]. Тем временем я выполнил код в своей среде, и он каким-то образом ограничивает извлекаемые результаты, например, когда я выполняю исходный скрипт для 1 базы данных, он дает мне более 100 столбцов с min_date. Однако ваш вышеприведенный скрипт извлекает только 43 записи для всех баз данных вместе. Есть ли там фильтр, ограничивающий результаты?

Vineeth Nair 21.02.2023 05:43

@VineethNair Я заметил проблему, попробуйте обновленный запрос.

Aaron Bertrand 21.02.2023 06:09

После сравнения результатов кажется, что скрипт выдает 1 строку вывода для каждой базы данных. Итак, изначально, если моя база данных имеет более 100 столбцов (в нескольких таблицах), которые соответствуют критериям, вместо того, чтобы выводить 100 x количество БД (в данном случае 43). Это просто дает мне 43 строки вывода. Столбцы также, кажется, выбираются случайным образом для каждой БД.

Vineeth Nair 21.02.2023 06:53

Извините, я пропустил ваше редактирование запроса. Теперь он работает нормально. Еще раз спасибо :)

Vineeth Nair 21.02.2023 06:56

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