从 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 你有。现在,您正在向代码中注入未经处理的参数,并且还使用了太大的数据类型。 @Columns
是 varchar(MAX)
,这意味着某人有 2GB 的字符可以注入您的系统。 @IdxColumn
是一个 varchar(250)
并引用单个列;一个对象的长度最多为 128 个字符,因此不需要其他 122 个字符。另外 @Limit
是一个 varchar
,尽管它是一个 int
并且应该是一个参数。
首先,我建议使用 table 类型的对象,而不是使用 varchar(MAX)
作为 @Columns
:
CREATE TYPE dbo.ObjectList (ObjectName sysname);
sysname
是nvarchar(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;
帮助我更快地执行此动态语句,该语句将从 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 你有。现在,您正在向代码中注入未经处理的参数,并且还使用了太大的数据类型。 @Columns
是 varchar(MAX)
,这意味着某人有 2GB 的字符可以注入您的系统。 @IdxColumn
是一个 varchar(250)
并引用单个列;一个对象的长度最多为 128 个字符,因此不需要其他 122 个字符。另外 @Limit
是一个 varchar
,尽管它是一个 int
并且应该是一个参数。
首先,我建议使用 table 类型的对象,而不是使用 varchar(MAX)
作为 @Columns
:
CREATE TYPE dbo.ObjectList (ObjectName sysname);
sysname
是nvarchar(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;