从 SQL Table 女士的每个单独列中获得最高 "n" 的动态语句

Dynamic Statement to Get Top "n" for Each Individual Column from Ms SQL Table

帮助我更快地执行此动态语句,该语句将从 table.

中获取每一列的前 n 个值

table 将有 "n" 列,但会有一个主键。 NULL 无法避免,因为任何其他值都被认为是有效的,应该进入数据库。

Table

+-------+------+------+------+
| Depth | RPMA | ROP  | WOB  |
+-------+------+------+------+
|  6111 |   72 | 14.6 | 0    |
|  6110 |   72 | 14.1 | 1    |
|  6109 |   66 | 15.2 | NULL |
|  6108 |   68 | 14   | NULL |
|  6107 |   69 | 14   | NULL |
|  6106 |   61 | 14.8 | NULL |
|  6105 |   70 | NULL | NULL |
|  6104 |   64 | NULL | NULL |
|  6103 |   59 | NULL | NULL |
|  6102 |   49 | NULL | NULL |
+-------+------+------+------+

结果集,

+-------+------+------+------+
| Depth | RPMA | ROP  | WOB  |
+-------+------+------+------+
|  6111 | 72   | NULL | 0    |
|  6110 | 72   | NULL | 1    |
|  6109 | NULL | 15.2 | NULL |
|  6106 | NULL | 14.8 | NULL |
+-------+------+------+------+

动态SQL用于获取当前结果集,

DECLARE @Columns VARCHAR(MAX); -- Param1
DECLARE @IdxColumn VARCHAR(250); --Param2
DECLARE @Limit VARCHAR(11); --Param3
DECLARE @SQL NVARCHAR(MAX)=''; --Param4

DECLARE @query NVARCHAR(MAX) = ' SELECT TOP (' + @pLimit + ') ' + @IdxColumn + ', ' + @Columns + ' FROM [Table] WHERE '

SET @SQL = @query + REPLACE(@Columns,',', ' IS NOT NULL ORDER BY '+ @IdxColumn + ' ASC ' + N' UNION' + @query) + ' IS NOT NULL ORDER BY ' + @IdxColumn

SET @SQL = 'SELECT * FROM ('+@SQL+') T ORDER BY ' + @IdxColumn + ' ASC'   

EXEC (@SQL)

以下查询应该适用于示例数据:

WITH cte AS (
  SELECT *
       , DENSE_RANK() OVER (ORDER BY RPMA DESC) AS RPMA_RANK
       , DENSE_RANK() OVER (ORDER BY ROP DESC) AS ROP_RANK
       , DENSE_RANK() OVER (ORDER BY WOB DESC) AS WOB_RANK
  FROM t
)
SELECT Depth
     , CASE WHEN RPMA_RANK <= 2 THEN RPMA END
     , CASE WHEN ROP_RANK <= 2 THEN ROP END
     , CASE WHEN WOB_RANK <= 2 THEN WOB END
FROM cte
WHERE RPMA_RANK <= 2
OR ROP_RANK <= 2
OR WOB_RANK <= 2

请注意,它 returns 三行用于 RPMA 列(有两个 72 和一个 70)。对于 n 列,您需要使用动态 SQL.

这并没有回答问题,但是修复了上述可怕的安全漏洞。

以上内容存在多个问题,因此请注意,这是一个 重要的 需要 更改为 SQL 你有。现在,您正在向代码中注入未经处理的参数,并且还使用了太大的数据类型。 @Columnsvarchar(MAX),这意味着某人有 2GB 的字符可以注入您的系统。 @IdxColumn 是一个 varchar(250) 并引用单个列;一个对象的长度最多为 128 个字符,因此不需要其他 122 个字符。另外 @Limit 是一个 varchar,尽管它是一个 int 并且应该是一个参数。

首先,我建议使用 table 类型的对象,而不是使用 varchar(MAX) 作为 @Columns

CREATE TYPE dbo.ObjectList (ObjectName sysname);

sysnamenvarchar(128) NOT NULL的同义词;并且是 SQL 服务器中用于对象名称的数据类型。然后,您需要将 INSERT 列的名称放入已声明的 table 类型参数中;每个列名称一行

然后我们可以安全地注入和参数化您的查询:

--Parameters
DECLARE @Columns dbo.ObjectList,
        @IdxColumn sysname, --sysname as well
        @Limit int; --not varchar

--Variables needed in the SQL:

DECLARE @SQL nvarchar(MAX),
        @CRLF nchar(2) = NCHAR(13) + NCHAR(10);

SET @SQL = N'SELECT TOP (@Limit)' + @CRLF + 
           N'           ' + QUOTENAME(@IdxColumn) + N',' + @CRLF +
           STUFF((SELECT N',' + @CRLF +
                         N'           ' + QUOTENAME(C.ObjectName)
                  FROM @Columns C
                  FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + @CRLF +
           N'FROM dbo.[Table]' + @CRLF + --Should dbo.[Table] also not be safely injected?
           N'WHERE ' +
           STUFF((SELECT @CRLF + 
                         N'   OR ' + QUOTENAME(C.ObjectName) + N' IS NOT NULL'
                  FROM @Columns C
                  FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,8,N'') + @CRLF + 
          N'ORDER BY ' + QUOTENAME(@IdxColumn) + N' ASC;'

EXEC sp_executesql @SQL, N'@Limit int', @Limit;