Я написал следующий запрос:
IF OBJECT_ID ('tempdb..#ColumnsType') IS NOT NULL DROP TABLE #ColumnsType
DECLARE @vQuery NVARCHAR(MAX) =''
IF OBJECT_ID ('tempdb..#random') IS NOT NULL DROP TABLE #random
CREATE TABLE #random (
ColumnID INT PRIMARY KEY IDENTITY(1,1) NOT NULL
, randomname VARCHAR(50)
, randomvalue INT)
INSERT INTO #random (randomname, randomvalue)
VALUES ('a3', 123)
, ('bla', 4325)
, ('another_bla', 5643)
, ('end_here', 3)
select *
from #random
CREATE TABLE #ColumnsType (
ColumnID INT PRIMARY KEY IDENTITY(1,1) NOT NULL
, ColumnName sysname
, DataType sysname
)
INSERT INTO #ColumnsType (ColumnName, DataType)
SELECT [name],
system_type_id
FROM Tempdb.Sys.Columns
WHERE Object_ID = Object_ID('tempdb..#random')
AND system_type_id = 56
DECLARE @i INT = (SELECT MIN(ColumnID) FROM #random);
DECLARE @maxId INT = (SELECT MAX(ColumnID) FROM #random);
DECLARE @ColumnName VARCHAR(200);
DECLARE @DataType VARCHAR(200);
WHILE @i <= @maxId
BEGIN
SET @ColumnName = (SELECT ColumnName FROM #ColumnsType WHERE ColumnId = @i)
-- SET @DataType = (SELECT DataType FROM #ColumnsType WHERE ColumnId = @i)
SELECT @vQuery =
'SELECT
MIN(TRY_CONVERT(NUMERIC(30, 4), ' +@ColumnName+ ')) AS ' +@ColumnName+ '_MinValue
, MAX(TRY_CONVERT(NUMERIC(30, 4), ' +@ColumnName+ ')) AS ' +@ColumnName+ '_MaxValue
, AVG(TRY_CONVERT(NUMERIC(30, 4), ' +@ColumnName+ ')) AS ' +@ColumnName + '_AvgValue
, STDEV(TRY_CONVERT(NUMERIC(30, 4), ' +@ColumnName+ ')) AS ' +@ColumnName+ '_StandardDeviation
, SUM(TRY_CONVERT(NUMERIC(30, 4), ' +@ColumnName+ ')) AS ' +@ColumnName+ '_TotalSum
FROM tempdb..#random' -- +@Schema+'.'+@Table+ ''
EXEC sp_executesql @vQuery
PRINT @vQuery
SET @i = @i + 1
END
Для демонстрации я создаю временную таблицу со случайными значениями. Я выполняю профилирование части столбцов, состоящих только из числовых значений. Чтобы отфильтровать столбцы, я получаю их имена и фильтрую по типу, используя Tempdb.Sys.Columns. В обычном случае с моими исходными данными я использую INFORMATION_SCHEMA.COLUMNS, но я думаю, что это не так важно.
Запрос возвращает следующее:
Результат представлен в двух строках. Что я хотел бы сделать, так это получить этот результат в одной строке. Идея состоит в том, чтобы повернуть результат одной строки после и получить следующий результат:
@Larnu Но как я могу применить UNION ALL к одному динамическому запросу, который возвращает результаты отдельно?
Поскольку вы изменяете свой динамический запрос, чтобы он не «запускал столбец SQL по столбцу, сделайте его одним оператором для всех ваших столбцов». Проблема в том, что вы мыслите программно и используете цикл.
Это то, что я не знаю, как сделать. И я только придумал это не оптимальное решение. Не могли бы вы показать пример того, что вы имеете в виду?


Как я уже упоминал, вам не нужно использовать цикл, использовать метод на основе набора и UNION ALL ваши динамические операторы. Я также предполагаю, что вы используете последнюю версию SQL Server. Если нет, вам нужно заменить STRING_AGG на старый метод FOR XML PATH (и STUFF).
Этого должно быть достаточно для начала:
USE Sandbox;
GO
CREATE TABLE dbo.YourTable (Col1 int,
Col2 varchar(10));
GO
DECLARE @SchemaName sysname = N'dbo',
@TableName sysname = N'YourTable';
DECLARE @SQL nvarchar(MAX),
@CRLF nchar(2) = NCHAR(13) + NCHAR(10);
DECLARE @Delimiter nvarchar(50) = @CRLF + N'UNION ALL' + @CRLF;
SELECT @SQL = STRING_AGG(CONVERT(nvarchar(MAX),N'SELECT MIN(') + QUOTENAME(c.[name]) + N') AS ' + QUOTENAME(c.[name] + N'_MIN') + N',' + @CRLF +
N' MAX(' + QUOTENAME(c.[name]) + N') AS ' + QUOTENAME(c.[name] + N'_MAX') + @CRLF +
N'FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name])
,@Delimiter) WITHIN GROUP (ORDER BY c.column_id)
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
WHERE s.[name] = @SchemaName
AND t.[name] = @TableName
PRINT @SQL;
EXEC sys.sp_executesql @SQL;
GO
DROP TABLE dbo.YourTable;
Я получаю эту ошибку: результат агрегации STRING_AGG превысил ограничение в 8000 байт. Используйте типы больших объектов, чтобы избежать усечения результатов. Кроме того, мне нужно фильтровать только числовые столбцы. Если я делаю это в операторе динамического запроса, это выдает ошибку, потому что он все еще проходит через нечисловые столбцы.
«Кроме того, мне нужно отфильтровать только числовые столбцы. Если я сделаю это в операторе динамического запроса, возникнет ошибка, потому что он все еще проходит через нечисловые столбцы». Этого не было в вашем вопросе, но это легко сделать. Добавьте еще одно JOIN к FROM к sys.types и еще одно предложение к WHERE, чтобы отфильтровать нечисловые столбцы.
Я получаю эту ошибку: результат агрегации STRING_AGG превысил ограничение в 8000 байт. Простое исправление, измените 'SELECT MIN(' на CONVERT(nvarchar(MAX),'SELECT MIN(')
Ну, это не тот результат, который я искал, что-то вроде этого: Col1_MIN Col1_MAX Col2_Min Col2_max, в один ряд. Я получаю строки для каждого столбца при запуске вашего решения.
Это не то, что показывает ваш пример изображения, @Yana, он показывает, что вам нужны 2 строки, а не 1.
Для этого вам понадобится динамический динамический (да, я сказал это дважды) SQL, а это путь к безумию, @Yana.
Я объясняю в описании, что хочу, чтобы это была одна строка. И мне очень жаль, если я сведу тебя с ума. Я не собираюсь этого делать.
Вы заявляете: «Идея состоит в том, чтобы повернуть результат одной строки после и получить следующий результат». Итак, чтобы получить результат на изображении, @Yana ... Это то, что делает вышеизложенное. Если это не то, что вам нужно, то ваш вопрос неясен, и вы (при всем уважении) потратили мое время здесь впустую.
Теперь я понимаю, где произошло недоразумение. Вы не зря потратили время. Но мне нужно время, чтобы понять, что вы сделали с STRING_AGG. Первый раз вижу его в использовании, никогда не пользовался. Спасибо и еще раз извините.
Вот очень чистое и красивое решение, которое я искал:
Единственное, что осталось сделать, это UNPIVOT, чтобы вставить результаты в нужную мне таблицу.
DECLARE
@q1 NVARCHAR(MAX)
, @q2 NVARCHAR(MAX)
, @q3 NVARCHAR(500)
, @schema VARCHAR(50) = '' -- choose schema
, @table VARCHAR(200) = '' -- choose table
SET @Q1 = 'SELECT ' + '''' + @table + '''' + ' as tableName, '
SET @Q3 = ' FROM ' + @schema + '.' + @table
SELECT @q2 = COALESCE(@q2 + ', ', '')
+ ' max(' + columnName + ') as ' + columnName + '_max, '
+ ' min(' + columnName + ') as ' + columnName + '_min, '
+ ' avg(' + columnName + ') as ' + columnName + '_avg, '
+ ' stdev(' + columnName + ') as ' + columnName + '_stdev, '
+ ' sum(' + columnName + ') as ' + columnName + '_sum '
FROM (
SELECT s.[name] as schemaName, t.[name] as tableName, c.[name] as columnName, st.[name] as typeName
FROM sys.schemas s
INNER JOIN sys.tables t ON s.schema_id = t.schema_id
INNER JOIN sys.columns c ON t.object_id = c.object_id
INNER JOIN sys.types st ON st.user_type_id = c.user_type_id
WHERE 1=1
AND s.[name] = @schema
AND t.[name] = @table
AND st.[name] IN ('') -- choose columns of specific data type, that you want to profile
) data
SELECT @q1 = @q1 + @q2 + @q3
EXEC sys.sp_executesql @Q1
Наслаждаться!
UNION ALLваши наборы данных. Не выполняйте SQL столбец за столбцом, сделайте его одним оператором для всех ваших столбцов.