需要一行中的所有列

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 

尽情享受吧!