#sql #sql-server #tsql
Вопрос:
Вывод, к которому я пытаюсь добраться;
(Имя базы данных = ATT)
- Имя таблицы
- Имя столбца
- МАКСИМАЛЬНАЯ дата загрузки = MAX(loaded_date) только для этого столбца
loaded_date-это столбец примерно в 50 таблицах базы данных с одинаковым именем и типом данных (Datetime).
select * FROM sys.tables
select * FROM syscolumns
Я без особого успеха изучал системные таблицы, просматривая некоторые сообщения, в которых может быть сделан динамический SQL, чего я никогда не делал.
Комментарии:
1. Что это
MAX(loaded_date) for this column only
значит? Естьloaded_date
еще одна колонка? Как бы выглядели результаты? Ввод образцов также помог бы2. Мой плохой Дейл . я не должен был добавлять **только для этой колонки
3. Тогда что же должен представлять собой пункт 2? Просто не понимаю, какие реальные результаты вы хотите видеть.
Ответ №1:
Вы можете написать sql, который пишет sql..
SELECT REPLACE(
'select ''{tn}'' as table_name, max(loaded_date) as ld from {tn} union all'
,'{tn}',table_name)
FROM
information_schema.columns
WHERE
column_name = 'loaded_date'
Запустите это, затем скопируйте все, кроме окончательного ОБЪЕДИНЕНИЯ, из окна результатов в окно запроса и запустите снова
Если бы вы хотели собрать все это в одну строку для dynamic exec, я думаю, это выглядело бы как (непроверенная) процедура, содержащая:
DECLARE @x NVARCHAR(MAX);
SELECT @x =
STRING_AGG(
REPLACE(
'select ''{tn}'' as table_name, max(loaded_date) as ld from {tn}'
,'{tn}',table_name)
,' union all ')
FROM
information_schema.columns
WHERE
column_name = 'loaded_date';
EXECUTE sp_executesql @x;
Если ваш SQLS старый и в нем нет string_agg, это немного неудобно, но в sql server есть много примеров «преобразования строк в CSV», которые выглядят так STUFF..FOR XML PATH
: https://duckduckgo.com/?t=ffabamp;q=rows к CSV SQLS и ia=веб
Комментарии:
1. Спасибо, Кай Джард, я действительно ценю все вышесказанное. Я запустил код, и он выводит нужные мне столбцы, однако, как я могу запустить это, не копируя его . я пытаюсь автоматизировать процесс, возможно, даже вставить выходные данные в отдельную таблицу позже . о чем ты думаешь ?
2. Эм … Это сильно зависит от того, какой код будет его запускать. Если это язык интерфейса, такой как C# или JS, то вы можете просто выбрать его таким, как он есть, превратить его в одну большую строку и запустить его, как любой другой SQL. Аналогично, если вы делаете это, как в хранимой процедуре, вы можете собрать все это в тип данных nvarchar(Max), а затем exec для него — если ваш SQL-сервер является последним, возможно STRING_AGG (я выложу представление о нем), который облегчит тоже… но это вроде как у вас может быть недостаток данных моделей здесь.. У вас есть эти данные, разбросанные по множеству таблиц — ПОЧЕМУ? вот что мне интересно.. Обычно..
3. ..когда люди говорят, что они затем отвечают: «потому что у меня 50 таблиц, которые все идентичны, но это одна таблица на клиента» или что-то в этом роде, и имя таблицы, таким образом, стало своего рода устройством разделения, что tbh является довольно ужасной идеей, потому что это приводит к этим неприятным взломам. В имени таблицы хранятся данные, которые должны быть в столбце в одной таблице — было бы лучше, если бы все 50 таблиц были объединены в 1 с дополнительным столбцом CLIENT_NAME или чем-то еще, чтобы разделить клиентов. Если вам необходимо обеспечить совместимость с устаревшими версиями, вы также можете создать набор представлений, которые похожи ..
4. ..
CREATE VIEW tblMicrosoft AS SELECT * FROM AllClients WHERE ClientName = 'Microsoft'
— и приложения могут продолжать выбирать его (или даже вставлять в него, используя вместо триггера), пока вы медленно обновляете всю систему для использованияAllClients
. Конечно, есть вероятность, что это не относится к вам, но если это так, и таблицы «имеют одинаковую структуру, но хранят данные отдельно по причине X», лучше этого не делать5. все таблицы разные, но поскольку база данных регулярно обновляется внешними третьими лицами, дата загрузки является последним столбцом, который находится в каждой таблице. аналогично тому, как DW имеет обновленную дату. простите меня, я все еще изучаю SQL .
Ответ №2:
Я написал более постоянный тип сценария, который делает это. Он возвращает результирующий набор списка таблиц в текущей базе данных с именем столбца loaded_date
вместе с MAX(loaded_date)
результатом из каждой таблицы. Этот скрипт индивидуально запрашивает каждую таблицу, просматривая и выполняя запрос для каждой таблицы по отдельности и отслеживая максимальное значение для каждой таблицы в табличной переменной. В нем также есть @Debug
переменная, которая позволяет вам видеть текст запросов, которые будут выполняться, вместо того, чтобы фактически запускать их, и реализует пользовательское сообщение об ошибке для устранения любых проблем.
/*disable row count messages*/
SET NOCOUNT ON;
/*set to 1 to debug (aka just print queries instead of running)*/
DECLARE @Debug bit = 0;
/*get list of tables to query and assign a unique index to row to assist in looping*/
DECLARE @TableList TABLE(
SchemaAndTableName nvarchar(257) NOT NULL
,OrderToQuery bigint NOT NULL
,MaxLoadedDate datetime NULL
,PRIMARY KEY (OrderToQuery)
);
INSERT INTO @TableList (SchemaAndTableName,OrderToQuery)
SELECT
CONCAT(QUOTENAME(s.name),N'.', QUOTENAME(t.name)) AS SchemaAndTableName
,ROW_NUMBER() OVER(ORDER BY s.name, t.name) AS OrderToQuery
FROM
sys.columns AS c
INNER JOIN sys.tables AS t ON c.object_id = t.object_id
INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
WHERE
c.name = N'loaded_date';
/*declare and set some variables for loop*/
DECLARE @NumTables int = (SELECT TOP (1) OrderToQuery FROM @TableList ORDER BY OrderToQuery DESC);
DECLARE @I int = 1;
DECLARE @CurMaxDate datetime;
DECLARE @CurTable nvarchar(257);
DECLARE @CurQuery nvarchar(max);
/*start loop*/
WHILE @I <= @NumTables
BEGIN
/*build text of current query*/
SET @CurTable = (SELECT SchemaAndTableName FROM @TableList WHERE OrderToQuery = @I);
SET @CurQuery = CONCAT(N'SELECT @MaxDateOut = MAX(loaded_date) FROM ', @CurTable, N';');
/*check debugging status*/
IF @Debug = 0
BEGIN
BEGIN TRY
EXEC sys.sp_executesql @stmt = @CurQuery
,@params = N'@MaxDateOut datetime OUTPUT'
,@MaxDateOut = @CurMaxDate OUTPUT;
END TRY
BEGIN CATCH
DECLARE @ErrorMessage nvarchar(max) = CONCAT(
N'Error querying table ', @CurTable, N'.', NCHAR(13), NCHAR(10)
,N'Errored query: ', NCHAR(13), NCHAR(10), @CurQuery, NCHAR(13), NCHAR(10)
,N'Error message: ', ERROR_MESSAGE()
);
RAISERROR(@ErrorMessage,16,1) WITH NOWAIT;
/*on error end loop so error can be investigated*/
SET @I = @NumTables 1;
END CATCH;
END;
ELSE /*currently debugging*/
BEGIN
PRINT(CONCAT(N'Debug output: ', @CurQuery));
END;
/*update value in our table variable*/
UPDATE @TableList
SET MaxLoadedDate = @CurMaxDate
WHERE
OrderToQuery = @I;
/*increment loop*/
SET @I = @I 1;
END;
SELECT
SchemaAndTableName AS TableName
,MaxLoadedDate AS Max_Loaded_date
FROM
@TableList;
Мне больше нравится это решение, так как запрос каждой таблицы по одному за раз окажет гораздо меньшее влияние на систему, чем попытка выполнить один большой UNION ALL
запрос. Одновременный запрос большого набора таблиц a может привести к серьезному конфликту между семафором ресурсов или блокировкой (в зависимости от использования вашей базы данных).
Это довольно хорошо прокомментировано, но дайте мне знать, если что-то не ясно.
Кроме того, просто обратите внимание, что динамический SQL следует использовать в качестве последнего средства. Я предоставил этот сценарий, чтобы ответить на ваш вопрос, но вам следует изучить лучшие варианты, чем что-то подобное.
Комментарии:
1. Привет, Трентон-ftw, Спасибо за вышесказанное, я действительно ценю это, я пробовал, и, как вы уже сказали, оно выдает только одно значение. мне нужно включить имя таблицы в вывод см. Ниже, например; Имя таблицы Max_Loaded_date таблица1 2021-07-16 таблица2 2021-07-15 таблица3 2021-07-16 таблица4 2021-07-14
2. @Huss1 ах, извините за это. Я неправильно понял. Я думал, ты хочешь по максимуму
loaded_date
за всеми столами. Я могу отредактировать этот сценарий.3. @Huss1 правильный ответ был обновлен, чтобы вернуть список таблиц с их
MAX(loaded_date)
вместо одного максимумаloaded_date
во всех таблицах.
Ответ №3:
Вы можете перейти к недокументированной хранимой процедуре sp_MSforeachtable
. Но не используйте в производственном коде, так как эта хранимая процедура может быть недоступна в будущих версиях.
Подробнее читайте в sp_MSforeachtable
EXEC sp_MSforeachtable 'SELECT ''?'' as tablename, max(loaded_Date) FROM ?'
Комментарии:
1. спасибо венкатараману-р, я попытался добавить союз, но все время получаю ошибку