需要一行中的所有列
Need all columns on one row
我写了以下查询:
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
为了演示,我创建了具有随机值的临时文件 table。我对仅由数值组成的部分列执行分析。要过滤列,我使用 Tempdb.Sys.Columns
获取它们的名称并按类型过滤。在我的原始数据的正常情况下,我使用 INFORMATION_SCHEMA.COLUMNS
但我认为这并不那么重要。
查询returns如下:
结果显示在两行中。我想做的是将这个结果排成一行。这个想法是在之后旋转一行结果并接收以下结果:
正如我提到的,您需要而不是使用循环,使用基于集合的方法和UNION ALL
您的动态语句。我在这里 假设 您使用的是最新版本的 SQL 服务器。如果没有,您需要用旧的 FOR XML PATH
(和 STUFF
)方法替换 STRING_AGG
。
这应该足以让您入门:
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;
这是我一直在寻找的一个非常干净漂亮的解决方案:
- 我从特定的table中选择列,扫描一次并对其执行多次计算
- 我使用动态查询并将其作为所有列的一条语句。
- 它运行得非常快。 table 有 5000 万行的 return 结果花了 5 分钟多一点。
唯一剩下要做的就是 UNPIVOT,以便将结果插入我想要的 table。
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
尽情享受吧!
我写了以下查询:
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
为了演示,我创建了具有随机值的临时文件 table。我对仅由数值组成的部分列执行分析。要过滤列,我使用 Tempdb.Sys.Columns
获取它们的名称并按类型过滤。在我的原始数据的正常情况下,我使用 INFORMATION_SCHEMA.COLUMNS
但我认为这并不那么重要。
查询returns如下:
结果显示在两行中。我想做的是将这个结果排成一行。这个想法是在之后旋转一行结果并接收以下结果:
正如我提到的,您需要而不是使用循环,使用基于集合的方法和UNION ALL
您的动态语句。我在这里 假设 您使用的是最新版本的 SQL 服务器。如果没有,您需要用旧的 FOR XML PATH
(和 STUFF
)方法替换 STRING_AGG
。
这应该足以让您入门:
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;
这是我一直在寻找的一个非常干净漂亮的解决方案:
- 我从特定的table中选择列,扫描一次并对其执行多次计算
- 我使用动态查询并将其作为所有列的一条语句。
- 它运行得非常快。 table 有 5000 万行的 return 结果花了 5 分钟多一点。
唯一剩下要做的就是 UNPIVOT,以便将结果插入我想要的 table。
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
尽情享受吧!